使用 crontab 和 pactl 更改系统音量

使用 crontab 和 pactl 更改系统音量

我正在尝试使用 crontab 更改 Ubuntu 14.04 系统卷。我使用以下命令:

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

从...开始Ubuntu 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用于登录 shell。
  • DISPLAY=:0.0用于显示
  • --server unix:/run/user/$(id -u)/pulse/native: (由于 cron 不在登录 shell 中,因此使用显式文件描述符)
  • @DEFAULT_SINK@ or 0:默认或第一个音频设备。对于特定设备,您可以使用完整的设备名称,例如alsa_output.pci-0000_00_1f.3.analog-stereo
  • on(或off):静音开/关

我必须在 Ubuntu 19.10 中添加的部分是 arg --server

答案3

从 cron 运行命令

在很多场合下都能正常工作,但当你需要或者想要运行 GUI 应用程序,或者在涉及环境变量的其他情况下,你可能需要花很多时间才能找到如何正确设置 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)

相关内容