Изменение громкости системы с помощью crontab и pactl

Изменение громкости системы с помощью crontab и pactl

Я пытаюсь изменить системную громкость 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, — это --serverarg.

решение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)

Связанный контент