Estou tentando alterar o volume do sistema Ubuntu 14.04 usando o crontab. Estou usando o seguinte comando:
pactl set-sink-volume 1 75%
O que funciona bem quando eu uso no terminal ou quando executo um script que inclui este comando, mas quando o sistema executa esse código no crontab ou via script que é executado no crontab, o sistema não altera o volume. Como posso consertar isso?
Eu também tentei
amixer -D pulse sset Master 75%
A aparência do Crontab (a cada minuto para fins de teste)
* * * * * pactl set-sink-volume 1 75%
ou
* * * * * /usr/bin/pactl set-sink-volume 1 75\%
Responder1
Demorei um pouco para descobrir, você pode fazer isso:
1 9 * * * export DISPLAY=:0 && amixer -D pulse sset Master 48000
1 22 * * * export DISPLAY=:0 && amixer -D pulse sset Master 32000
Em 100%, o volume é aproximadamente 64.000. Isso aumentará o volume em 9 e diminuirá em 22. Também no crontab do usuário, não no sudo.
Responder2
Começando comUbuntu 19.10, um simples DISPLAY=:0
(ou mesmo 0,0) não ajudou no cron, recebi este erro:
Connection failure: Connection refused
pa_context_connect() failed: Connection refused
Eu tive que fazer o seguinte (este é o meumudo à noitecron, mas você pode adaptar às suas necessidades):
30 21 * * * bash -l -c "DISPLAY=:0.0 pactl --server unix:/run/user/$(id -u)/pulse/native set-sink-mute @DEFAULT_SINK@ on"
Discriminação:
bash -l
para shell de login.DISPLAY=:0.0
para exibição--server unix:/run/user/$(id -u)/pulse/native
: (descritor de arquivo explícito, pois o cron não está em um shell de login)@DEFAULT_SINK@ or 0
: O padrão ou primeiro dispositivo de áudio. Para um dispositivo específico, você pode usar o nome completo do dispositivo, por exemploalsa_output.pci-0000_00_1f.3.analog-stereo
on
(ouoff
): ativar/desativar mudo
A peça que tive que adicionar no Ubuntu 19.10 foi o --server
arg.
Responder3
Executando comandos do cron
Funciona bem e confiável em muitas ocasiões, mas quando você precisa ou deseja executar, por exemplo, aplicativos GUI, ou em outras situações onde variáveis de ambiente estão envolvidas, pode ser uma grande pesquisa descobrir como configurar corretamente um (combinação de) cron trabalho(s) e para descobrirqualvariáveis de ambiente devem ser definidas e como.
Alternativa
Nestas situações, pode ser conveniente ter uma alternativa simples, executar um comando em um horário específico, ou mesmo executar um “programa diurno” completo, a partir do ambiente do usuário atual.
É isso que o script abaixo faz. Ele executa comandos e/ou aplicativos, listados em um arquivo de texto em formato simples, parecido com:
11:09,gedit
11:10,gnome-terminal
11:20,pactl set-sink-volume 1 10%
Eu testei com seu comando e funciona bem.
Como configurar
A configuração consiste em três pequenos arquivos, que você precisa armazenar em uma mesma pasta. Em um desses arquivos ( command_data.txt
), você precisa listar os comandos, juntamente com o horário em que deseja que os comandos sejam executados, só isso.
Use o seguinte formato:
time/comma/command (no spaces around the comma)
para aumentar o volume em 5 minutos para 100%, por exemplo:
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%
Os arquivos:
Como dito: os três arquivos devem estar localizados em uma mesma pasta.
arquivo 1, o script principal.
Copie-o para um arquivo vazio, salve-o como schedule.py
(mantenha o nome como está) e torne-o executável (importante)
#!/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)
arquivo 2, o script para iniciar/parar o agendamento.
Salve-o como run_schedule.py
(mantenha o nome como está) e torne-o executável (importante)
#!/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..."])
arquivo 3, crie um arquivo vazio, chamadocommand_data.txt
preencha-o com seus comandos conforme explicado em "Como configurar"
iniciar/parar (alternar) o agendamento pelo comando:
/path/to/run_schedule.py
Uma mensagem de notificação aparecerá:
ou:
Explicação
O que os arquivos fazem:
Quando o script schedule.py
é iniciado, ele lê os comandos e seu tempo de execução agendado no arquivo command_data.txt
. Em um loop, a hora atual é comparada com a hora agendada dos comandos listados. Se o horário atual for igual a um ou mais horários de trabalho agendados, o comando será executado e marcado como "concluído" para o horário atual.
O script run_schedule.py
verifica se o script principal ( schedule.py
) está em execução. Nesse caso, o trabalho é encerrado; caso contrário, o script é iniciado. Em ambos os casos é exibida uma notificação de confirmação.
Responder4
Obrigado por isso. Minha tela/áudio de movimento agora funciona quando o PIR é acionado.
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)