Windows

Как в Windows 10 автоматически выполнять задачи на восходе и закате солнца

В Windows 10 есть режим ночного света, меняющий свет экрана с дневного на более теплый и обратно. Это работает вручную, по фиксированному расписанию или на закате и восходе. И я не раз видел желающих применить именно последний вариант для смены цветов ОС или плана электропитания. Поскольку встроенного решения нет, эти люди обламываются и соглашаются на фиксированное время в планировщике заданий.

Приемы работы с ним я неоднократно показывал, и в этот раз применю, но чуть более изобретательно. Я подготовил набор скриптов PowerShell, которые создают необходимые задачи в планировщике и выполняют команды.


Для примера они задают на восходе светлые цвета ОС и приложений, а на закате – темные.

Вы сможете прописать свои команды в двух скриптах PowerShell или изменить созданные задачи в графическом интерфейсе taskschd.msc, настроив для себя запуск скриптов cmd, vbs и т.д. А для тех, кто захочет существенно изменить под свои цели исходные скрипты, в рассказе есть большой теоретический раздел.

Как это работает

Вкратце, схема несложная:

  1. В планировщике создается три задания.
  2. Основное задание от имени системы ежедневно получает из внешнего сервиса время восхода / заката и обновляет время запуска вспомогательных заданий.
  3. Вспомогательные задания от имени пользователя выполняются на восходе или закате.

В скриптах предусмотрены некоторые нюансы вроде выполнения пропущенной задачи без задержек.

Ограничения этого подхода

Чтобы все работало, необходимо соблюдения двух условий.


Доступность сервиса с гео-данными

Время восхода и заката берется с сервиса https://sunrise-sunset.org/. Эта ссылка была фактически первой в Google, но есть и другие бесплатные ресурсы — например, API метеорологического института Норвегии. Изменить скрипт под другой сервис очень просто, как вы увидите ниже.

Наличие у пользователя достаточных прав на выполнение команд

Вспомогательные задачи выполняются с наивысшими правами, доступными пользователю, от имени которого они запускаются. Аккаунту из группы администраторов все по плечу, но учетная запись с обычными правами не может выполнять задачи, требующие повышения прав.

В принципе, вспомогательные команды тоже можно выполнять от имени системы, что я разберу в теоретической части статьи. Но это годится только для изменения системных параметров (смена плана электропитания), и не подходит для внесения изменений в параметры текущего пользователя (смена цветовой схемы).

Практика

Я думаю, вам будет интереснее сразу перейти к практике, убедиться в работоспособности скриптов, а потом уже разбираться с теорией.

Содержимое архива

В архиве sunrise-sunset.zip четыре скрипта PowerShell.

СКРИПТНАЗНАЧЕНИЕ
set_tasks.ps1Создает три запланированных задания:
• Sunrise-Sunset-Time (основное)
• Sunrise
• Sunset
sunrise_sunset_time.ps1Выполняется от имени системы из основного задания: ежедневно по расписанию, при входе любого пользователя в систему и при выходе из сна/гибернации. Скрипт получает время восхода и заката, обновляет время запуска вспомогательных заданий и запускает их при необходимости.Например, если ПК был выключен вечером после заката, а включен днем после рассвета выполняется задание Sunrise.
sunrise.ps1Выполняется из задания Sunrise для запуска скрипта на рассвете.
sunset.ps1Выполняется из задания Sunset для запуска скрипта на закате.

Содержимое двух последних скриптов вы можете менять на свое усмотрение.


Создание задач в планировщике

  1. Скачайте архив sunrise-sunset.zip и распакуйте в папку, в которой в дальнейшем будут храниться скрипты. Если вы впоследствии поменяете путь к скриптам, придется править задачи в планировщике или пересоздавать их.
  2. Откройте скрипт sunrise_sunset_time.ps1 в текстовом редакторе, укажите широту и долготу своего местоположения (карты Google или Яндекс в помощь) и сохраните файл.
  3. В проводнике перейдите в папку со скриптами, из меню Файл выберите Запустить Windows Powershell от имени администратора и выполните команду ниже (также можно явно задать путь к папке параметром -Path):
powershell -ExecutionPolicy Bypass '.\set_tasks.ps1'

Это все! В планировщике появятся три задачи с именами, начинающимися на Sun. Если он был открыт во время выполнения команды, нажмите F5.

Запуск задач планировщика на рассвете и закате

Цвета ОС и приложений могут сразу же поменяться — например, они станут темными, если команда выполнялась после заката.

Изменение задач под себя (основы)

Вам достаточно прописать свои команды PowerShell в скриптах sunrise.ps1 и sunset.ps1. Сейчас там просто изменяются два параметра реестра, например, на закате:

#Переключиться на темные цвета
#Windows
New-ItemProperty -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize -Name SystemUsesLightTheme -Value 0 -Type Dword -Force
#приложений
New-ItemProperty -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize -Name AppsUseLightTheme -Value 0 -Type Dword -Force

Если вас это чем-то смущает или не устраивает, откройте одноименные задания в планировщике и пропишите нужные команды и их аргументы на вкладке Действия. Например, в качестве команды вы можете просто указать полный путь к CMD-файлу.

Более детальный разбор заданий в теоретической части статьи.


Теория

Этот раздел для тех, кто хочет лучше понять, как все работает, а также разобраться в нюансах планирования задач с PowerShell и содержимом скриптов. Их по сути два:

  • sunrise_sunset_time.ps1 – это небольшой скрипт, который получает гео-данные, обновляет время запуска задач и запускает их, если необходимо.
  • set_tasks.ps1 используется однократно для создания задач планировщика. Он выглядит объемнее, но лишь из-за специфики командлетов модуля scheduledtasks и количества комментариев.

Получение и обработка гео-данных

Сервис Sunrise-Sunset отдает JSON по ссылке, принимающей параметры широты, долготы и даты.

$lat = "55.753595"
$lng = "37.621031"
$today = Get-Date -f "yyyy-MM-dd"
$j = Invoke-RestMethod "https://api.sunrise-sunset.org/json?lat=$($lat)&lng=$($lng)&date=$($today)&formatted=0"
$j.results.sunrise

В API поддерживается указание сегодняшнего дня параметром &date=today, однако сервис завязан на UTC. Поэтому на запрос с таким параметром, например, в 00:01 по Москве, сервис ответит вчерашними данными по московскому времени.

Поэтому в скрипте дата задается явно с помощью формата командлета Get-Date, что я уже показывал в блоге. Затем командлет Invoke-RestMethod получает данные, а дальше просто выбираются сведения из нужного узла. Для наглядной работы с узлами можно отформатировать полученный JSON.

Аналогично для сервиса норвежского метеоинститута команды были бы такие:

$n = Invoke-RestMethod -uri "https://api.met.no/weatherapi/sunrise/2.0/.json?lat=$($lat)&lon=$($lng)&date=$($today)&offset=+03:00"
$n.location.time.sunrise.time

Это было просто.


Создание и изменение запланированных заданий в PowerShell

Я не раз показывал в блоге работу с schtasks (вот хороший пример). С помощью ключей schtasks формируются параметры задания, а само оно создается, опрашивается, изменяется или удаляется одной командой. Без справки с ключами не разобраться, но в итоге получается команда в одну строку.

В PowerShell сделаны отдельные командлеты для работы с заданиями, в частности для:

  • управления (создание, удаление, экспорт и т.д.)
  • настройки, т.е. как минимум по одному командлету для каждой вкладки задания планировщика, если апеллировать к графическому интерфейсу (параметры командлетов уже определяют конкретные настройки на вкладке)

В результате для создания или изменения задачи требуется несколько строк кода. И хотя названия командлетов лучше понятны интуитивно, чем ключи schtasks, от всего этого рябит в глазах.

Создание

Давайте посмотрим на процесс создания задачи Sunrise-Sunset-Time (фрагмент скрипта set_tasks.ps1). Переменные фактически соответствуют английским названиям вкладок в интерфейсе планировщика, а код я подробно прокомментировал.

#Переменные
$path = (Get-Location).path
$system = "NT AUTHORITY\SYSTEM"
#Создание задания
$taskname = "Sunrise-Sunset-Time"
#Общие: выполнять с наивысшими правами от имени системы вне зависимости от входа
$principal = New-ScheduledTaskPrincipal -UserId $system -LogonType ServiceAccount
#Триггеры
$trigger = @(
    #Запускать ежедневно сразу после полуночи (не пробуждая ПК)
    $(New-ScheduledTaskTrigger -Daily -At 00:01),
    #Запускать при входе пользователя в систему
    $(New-ScheduledTaskTrigger -AtLogon)
)
#Параметры: запускать при работе от батареи; запускать немедленно если пропущено
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -StartWhenAvailable
#Команда...
$execute = "powershell"
#... и ее параметры командной строки
$argument = "-ExecutionPolicy Bypass -WindowStyle Hidden -file $path\sunrise_sunset_time.ps1"
#Действие: "команда + параметры командной строки"
$action = New-ScheduledTaskAction -Execute $execute -Argument $argument
#Создать задание в планировщике
Register-ScheduledTask -TaskName $taskname -Action $action -Trigger $trigger -Settings $settings -Principal $principal

Читать это можно снизу вверх. Сама задача создается командлетом Register-ScheduledTask. Но сначала ее настройки формируются с помощью нескольких командлетов. При этом у New-ScheduledTaskAction, отвечающего за действие задачи, предусмотрены раздельные параметры для команды и ее ключей.

Также обратите внимание, как задается несколько триггеров одновременно. К сожалению, у командлета New-ScheduledTaskTrigger не предусмотрен параметр для триггера по событию в журнале. Поэтому триггер на событие выхода из сна / гибернации добавляется обходным путем уже после создания задачи (см. код в скрипте).


Изменение

С этим несколько проще. Задача Sunrise-Sunset-Time запускает скрипт sunrise_sunset_time.ps1, в котором есть такой фрагмент:

$j = Invoke-RestMethod "https://api.sunrise-sunset.org/json?lat=$($lat)&lng=$($lng)&date=$($today)&formatted=0"
$sunrise = New-ScheduledTaskTrigger -Once -At $j.results.sunrise
Set-ScheduledTask -TaskName "Sunrise" -Trigger $sunrise

Полученное из гео-сервиса время заносится в переменную, а она в свою очередь используется для формирования триггера командлетом New-ScheduledTaskTrigger. Затем этот триггер передается командлету Set-ScheduledTask, изменяющему задание.

Разобраться в модуле scheduledtasks с наскока затруднительно. Надо практиковаться, читать документацию и гуглить в тяжелых случаях.

Изменение заданий под себя (нюансы планировщика)

Есть несколько моментов, которые нужно учитывать в зависимости от ваших целей и пользователей, для которых должны отрабатывать задания. Это больше относится к планировщику, нежели к PowerShell.

Запуск в зависимости от выполненного входа

Основное задание запускается от имени SYSTEM вне зависимости от входа. Для такого случая у командлета New-ScheduledTaskPrincipal есть параметр -LogonType, задающий служебный аккаунт.

$system = "NT AUTHORITY\SYSTEM"
$principal = New-ScheduledTaskPrincipal -UserId $system -LogonType ServiceAccount

Вспомогательные задания выполняются в контексте пользователя, только когда он выполнил вход. Допустим, в некий момент времени ПК включен, но пользователь не вошел в систему. В этом случае основной скрипт отработает и обновит время запуска вспомогательных. Дальше допустим, что пользователь вошел в систему позже запланированного времени.

Запуск задач планировщика на рассвете и закате

Если бы у основного задания был только триггер на расписание, пропущенные задания выполнялись бы спустя несколько минут после входа пользователя. Однако благодаря триггеру на вход в систему, основной скрипт снова запустится и форсирует запуск вспомогательного, если необходимо. Аналогично работает и сценарий выхода из сна/гибернации.

Если вы хотите выполнять команды от имени своей учетной записи вне зависимости от ее входа, на вкладке Общие нажмите Выполнять для всех пользователей – ОК – введите пароль своего аккаунта.

Запуск задач планировщика на рассвете и закате

Если ваши команды изменяют параметры системы, а не пользователя (например, меняют план электропитания), вы можете перевести вспомогательные задания в системный контекст. Для этого на вкладке Общие нажмите Изменить и введите имя СИСТЕМА или SYSTEM в зависимости от языка вашей ОС.


Запуск в контексте другого пользователя

Скрипт set-tasks.ps1 создает вспомогательные задания для пользователя, который его запускает, т.е. для вашей учетной записи. Вы можете указать вместо себя другого пользователя при запуске скрипта параметром -User в формате "Имя_компьютера\Имя_пользователя". Например, для пользователя на картинке выше:

powershell -ExecutionPolicy Bypass '.\set-tasks.ps1' -User "DESKTOP-EHPD2RM\Admin" -Path "C:\MyScripts"

Если задания уже созданы, на вкладке Общие нажмите Изменить и введите имя пользователя.

Запуск для нескольких пользователей

Чтобы вспомогательные скрипты работали для нескольких пользователей в их контексте, можно создавать события в журнале. Либо так:

  1. В планировщике экспортировать задачи Sunrise и Sunset и импортировать их с другими именами, а затем указать для клонов нужных пользователей.
  2. Добавить в скрипт sunrise_sunset_time.ps1 команды для обновления времени клонов и их запуска в сценарии «пропущено назначенное время выполнения задания».

Если вам это необходимо, попрактикуйтесь самостоятельно.

Запуск без моргания окон PowerShell

Несмотря на -WindowStyle Hidden в аргументах скриптов, при их запуске из планировщика могут появляться окна PowerShell.

Этого не происходит для заданий, которые выполняются вне зависимости от входа пользователя в систему (в частности, для основного скрипта Sunrise-Sunset-Time).

Отсюда следует несколько решений на выбор для вспомогательных заданий. Вы можете:

  • выполнять их от имени системы, если изменяются параметры ОС, а не пользователя
  • выполнять их от своего имени, сохранив учетные данные
  • запускать из планировщика скрипт VBS, из которого в свою очередь вызывается скрипт PowerShell

Первые два способа я рассмотрел выше, а третий  показывал в своем канале Telegram


Диагностика

Концептуально решение должно работать начиная с Windows 7 (с WMF 5.1), но я тестировал только на русской и английской Windows 10 1909. На случай, если что-то пойдет не так, я подобрал рекомендации по отладке скриптов и заданий планировщика.

В рамках диагностики:

  • выполняйте все команды и скрипты от имени администратора
  • после запуска заданий планировщика нажимайте F5, чтобы обновить картину

Основное задание не обновляет вспомогательные

  1. Выполните задание вручную и убедитесь, что изменилось время его последнего запуска, а операция завершилась успешно. Ошибки могут означать недостаток прав на выполнение команды или неверную конфигурацию действия.
  2. Измените время запуска вспомогательного задания и запустите основное, а затем проверьте, поменялось ли время вспомогательного.
  3. Если время не изменилось, возможно, не удается достучаться до гео-сервиса. Откройте ссылку ниже в браузере, и если она открывается, выполните в PowerShell:
Invoke-RestMethod 'https://api.sunrise-sunset.org/json?lat=55.751510&lng=37.618877&date=today&formatted=0'

Если данные отображаются, нужно разбираться с дальнейшей логикой скрипта sunrise_sunset_time.ps1.

Вспомогательные задания не работают

  1. См. п.1 выше.
  2. Выполняйте скрипты sunrise.ps1 и sunset.ps1 или их команды в PowerShell и смотрите, что происходит. Если не работает, дело не в планировщике.
  3. Пробуйте прописать в скриптах что-нибудь простое, например, запись текущей даты в текстовый файл:
Get-Date | Out-File -FilePath C:\scripts\test.txt -Force

4. Если это работает, выполняйте одноименные задания в планировщике.

Помощь

Если вам необходима помощь, предоставьте в комментариях:

  1. Экспортированные задания планировщика плюс скрипты sunrise.ps1 и sunset.ps1 (если меняли). Все должно быть упаковано в ZIP и залито на Яндекс.Диск / OneDrive / Google Drive.
  2. Команды и ошибки при их выполнении в текстовом виде на https://pastebin.com/, в скриншотах – на хостинге картинок.
  3. Подробное описание проблемы и отчет по мотивам диагностики выше.

Заключение

Если бы в планировщике уже было некое задание с триггерами на восходе и закате, можно было бы цепляться к нему или клонировать путем экспорта/импорта, но я такого не нашел. Наверное, ночной свет использует API встроенной в систему службы местоположения, что было бы логично.

Скрипт создания задач планировщика выглядит сложновато для неподготовленного человека. Но лезть в него необязательно, поскольку параметры созданных задач можно изменять в графическом интерфейсе taskschd.msc. Все остальное укладывается в дюжину строк кода PowerShell.

При этом мой подход несложно адаптировать под другие задачи, выполняющиеся на основе различных гео-данных, прогноза погоды и вообще любых сведений, подтягиваемых из интернета.

Из JSON очень легко извлекать необходимые сведения, а в более сложных случаях можно обрабатывать вывод Invoke-WebRequest.


Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *