Я пытаюсь изменить системную громкость Ubuntu 14.04 с помощью crontab. Я использую следующую команду:
pactl set-sink-volume 1 75%
Который отлично работает, когда я использую его в терминале или когда я запускаю скрипт, который включает эту команду, но когда система запускает этот код из crontab или через скрипт, который работает в crontab, система не меняет громкость. Как я могу это исправить?
Я тоже пробовал
amixer -D pulse sset Master 75%
Crontab выглядит так (каждую минуту для целей тестирования)
* * * * * pactl set-sink-volume 1 75%
или
* * * * * /usr/bin/pactl set-sink-volume 1 75\%
решение1
Мне потребовалось некоторое время, чтобы разобраться, можно ли сделать так:
1 9 * * * export DISPLAY=:0 && amixer -D pulse sset Master 48000
1 22 * * * export DISPLAY=:0 && amixer -D pulse sset Master 32000
При 100% громкости это примерно 64000. Это увеличит громкость до 9 и уменьшит до 22. Также в вашем пользовательском crontab, а не в sudo.
решение2
Начиная сУбунту 19.10, простой DISPLAY=:0
(или даже 0.0) в cron не помог, я получил эту ошибку:
Connection failure: Connection refused
pa_context_connect() failed: Connection refused
Вместо этого мне пришлось сделать следующее (Это мойотключать звук ночьюcron, но вы можете адаптировать его под свои нужды):
30 21 * * * bash -l -c "DISPLAY=:0.0 pactl --server unix:/run/user/$(id -u)/pulse/native set-sink-mute @DEFAULT_SINK@ on"
Авария:
bash -l
для оболочки входа.DISPLAY=:0.0
для отображения--server unix:/run/user/$(id -u)/pulse/native
: (явный дескриптор файла, поскольку cron не находится в оболочке входа)@DEFAULT_SINK@ or 0
: Первое аудиоустройство или устройство по умолчанию. Для конкретного устройства можно использовать полное имя устройства, напримерalsa_output.pci-0000_00_1f.3.analog-stereo
on
(илиoff
): включить/выключить звук
Часть, которую мне пришлось добавить в Ubuntu 19.10, — это --server
arg.
решение3
Запуск команд из cron
Работает отлично и надежно во многих случаях, но когда вам нужно или вы хотите запустить, например, приложения с графическим интерфейсом или в других ситуациях, где задействованы переменные среды, может потребоваться немало времени, чтобы выяснить, как правильно настроить (комбинацию) заданий cron, а также выяснить,которыйкакие переменные окружения следует задать и как.
Альтернатива
В таких ситуациях может быть удобно иметь простую альтернативу — запустить команду в определенное время или даже запустить полную «дневную программу» из среды текущего пользователя.
Вот что делает скрипт ниже. Он запускает команды и/или приложения, перечисленные в текстовом файле в простом формате, выглядящем так:
11:09,gedit
11:10,gnome-terminal
11:20,pactl set-sink-volume 1 10%
Я проверил это с помощью вашей команды, и все работает отлично.
Как настроить
Настройка состоит из трех небольших файлов, которые вам нужно сохранить в одной и той же папке. В одном из этих файлов ( command_data.txt
) вам нужно перечислить команды вместе со временем, когда вы хотите, чтобы команды были выполнены, вот и все.
Используйте следующий формат:
time/comma/command (no spaces around the comma)
чтобы увеличить громкость за 5 минут до 100%, например:
11:20,pactl set-sink-volume 1 0%
11:21,pactl set-sink-volume 1 20%
11:22,pactl set-sink-volume 1 40%
11:23,pactl set-sink-volume 1 60%
11:24,pactl set-sink-volume 1 80%
11:25,pactl set-sink-volume 1 100%
Файлы:
Как уже было сказано: три файла должны находиться в одной и той же папке.
файл 1, основной скрипт.
Скопируйте его в пустой файл, сохраните его как schedule.py
(оставьте имя как есть) и сделайте его исполняемым (важно)
#!/usr/bin/env python3
import subprocess
import time
import datetime
import os
cmd_data = os.path.dirname(os.path.abspath(__file__))+"/command_data.txt"
with open(cmd_data) as data:
s = [item.strip().split(",")+[None] for item in data.readlines()]
def currtime(set_time):
return int(set_time.split(":")[0])*60+int(set_time.split(":")[1])
def run_command(t, now, cmd, last_run):
if currtime(t) == now and last_run != int(time.strftime("%d%m%Y"))+int(now):
subprocess.Popen(["/bin/bash", "-c", cmd])
else:
pass
while True:
now = currtime(str(datetime.datetime.now().time())[:5])
for i in range(len(s)):
cmdata = s[i]
run_command(cmdata[0], now, cmdata[1], cmdata[2])
s[i][2] = int(time.strftime("%d%m%Y"))+int(now)
time.sleep(30)
файл 2, скрипт для запуска/остановки расписания.
Сохраните его как run_schedule.py
(оставьте имя как есть) и сделайте его исполняемым (важно)
#!/usr/bin/env python3
import os
import subprocess
script_dir = os.path.dirname(os.path.abspath(__file__))
cmd = "ps -ef | grep schedule.py"
run = subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8").split("\n")
match = [line for line in run if script_dir+"/"+"schedule.py" in line]
if len(match) != 0:
subprocess.Popen(["kill", match[0].split()[1]])
subprocess.Popen(["notify-send", "Schedule stopped..."])
else:
subprocess.Popen(["/bin/bash", "-c", script_dir+"/"+"schedule.py"])
subprocess.Popen(["notify-send", "Schedule runs..."])
файл 3, создайте пустой файл с именемcommand_data.txt
заполните его своими командами, как описано в разделе «Как настроить»
запустить/остановить (переключить) расписание командой:
/path/to/run_schedule.py
Появится уведомляющее сообщение:
или:
Объяснение
Что делают файлы:
При запуске скрипта schedule.py
он считывает команды и их запланированное время выполнения из command_data.txt
. В цикле текущее время сравнивается с запланированным временем перечисленных команд. Если текущее время равно одному или нескольким запланированным временам выполнения задания, команда выполняется и помечается как «выполненная» для текущего времени.
Скрипт run_schedule.py
проверяет, запущен ли основной скрипт ( schedule.py
). Если да, то задание останавливается, если нет, то скрипт запускается. В обоих случаях отображается подтверждающее уведомление.
решение4
Спасибо за это. Теперь мой экран движения/звук срабатывает при срабатывании PIR.
import time
import subprocess
from gpiozero import MotionSensor
import os
import logging
import time
import subprocess
os.environ["DISPLAY"] = ":0"
# Set up logging
logging.basicConfig(filename='output.log', level=logging.DEBUG,
format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
# Read in value of default_sink.txt for default_sink
with open('default_sink.txt', 'r') as f:
default_sink = f.read().strip()
print(default_sink)
# Constants
IR_SENSOR_PIN = 4 # GPIO pin number of IR sensor
TIMER_DURATION = 60 # Timer duration in seconds
# Initialize IR sensor and timer
ir_sensor = MotionSensor(IR_SENSOR_PIN)
timer = time.time() + TIMER_DURATION # Set timer to expire immediately
#run pactl command like this DISPLAY=:0.0 pactl --server unix:/run/user/1000/pulse/native set-sink-mute 0 on"
while True:
if ir_sensor.motion_detected:
logging.debug("Motion detected")
# Reset timer
timer = time.time() + TIMER_DURATION
# Turn on display and audio
subprocess.call("xset dpms force on", shell=True)
result = subprocess.run(f"DISPLAY=:0.0 pactl --server unix:/run/user/1000/pulse/native set-sink-mute '{default_sink}' 0", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
with open("output.log", "a") as f:
f.write(result.stdout.decode())
f.write(result.stderr.decode())
f.write(f"Return code: {result.returncode}\n")
elif timer and time.time() > timer:
logging.debug("Timer expired")
# Turn off display and audio
subprocess.call("xset dpms force off", shell=True)
result = subprocess.run(f"DISPLAY=:0.0 pactl --server unix:/run/user/1000/pulse/native set-sink-mute '{default_sink}' 1", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
with open("output.log", "a") as f:
f.write(result.stdout.decode())
f.write(result.stderr.decode())
f.write(f"Return code: {result.returncode}\n")
timer = time.time() + TIMER_DURATION # Set timer to expire immediately
time.sleep(1)