Python — ненадежное выполнение команд оболочки? Проблемы с выполнением команд ir-ctl

#python #python-3.x #shell #command-line #raspberry-pi

#python #python-3.x #оболочка #командная строка #малина-пи

Вопрос:

Я создаю простую автоматизацию для своего стереоусилителя с использованием Raspberry Pi, и у меня возникли некоторые проблемы с выполнением команд оболочки из python. Предполагается, что мой скрипт прослушивает события от клиента spotify и изменяет выбранный источник на моем усилителе. У меня есть IR blaster, подключенный к моему Raspberry Pi, которым я управляю с помощью ir-ctl инструмента. Это отлично работает из командной строки, например, когда я запускаю это:

 ir-ctl -d /dev/lirc0 -S necx:0x856a24
  

Источник на моем усилителе изменен на AUX. Тогда я могу запустить:

 ir-ctl -d /dev/lirc0 -S necx:0x856a8c
  

Чтобы изменить его обратно на CD. Это работает на 100% успешно при запуске вручную из оболочки. Устройство настроено через /boot/config.txt :

 dtoverlay=gpio-ir-tx
  

Теперь я хочу использовать это в скрипте python, который прослушивает события от моего клиента spotify и изменяет источник в соответствии с устройством, выбранным в spotify.
И вот в чем проблема — кажется, что скрипт выполняет команду, но иногда ничего не происходит, исходный код не изменен. Я в замешательстве по этому поводу, поскольку вижу стандартный вывод, подтверждающий успешное выполнение команды.

Вот мой скрипт с операциями усилителя:

 def sourceCD():
    print("Changing source to CD")
    runCode('0x856a8c')

def sourceAUX():
    print("Changing source to AUX")
    runCode('0x856a24')

def runCode(code):
    necCommand = 'necx:{}'.format(code)
    #result = subprocess.run(["ir-ctl", "-d","/dev/lirc0","-S", necCommand], capture_output=True, check=True)
    result = subprocess.run(['ir-ctl -d /dev/lirc0 -S necx:{}'.format(code)], shell=True, capture_output=True, check=True)
    print(result)
  

И вот мой сценарий прослушивания:

 def on_message(ws, message):
    event = json.loads(message)["event"]
    print('Received event: ', event)

    if event == "contextChanged":
        print("Reacting to changed context")
        sourceAUX()

    if event == "inactiveSession":
        print("reacting to inactive session")
        sourceCD()

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://192.168.0.41:24879/events",
                          on_message = on_message,
                          on_error = on_error,
                          on_close = on_close)
    ws.run_forever()
  

Я вижу в выводе консоли, что сценарии оболочки выполняются каждый раз, когда я меняю устройство spotify:

 Aug 23 23:09:59 raspberrypi python[4571]: Received event:  inactiveSession
Aug 23 23:09:59 raspberrypi python[4571]: reacting to inactive session
Aug 23 23:09:59 raspberrypi python[4571]: Changing source to CD
Aug 23 23:09:59 raspberrypi python[4571]: CompletedProcess(args=['ir-ctl', '-d', '/dev/lirc0', '-S', 'necx:0x856a8c'], returncode=0, stdout=b'', stderr=b'')
Aug 23 23:10:01 raspberrypi python[4571]: Received event:  contextChanged
Aug 23 23:10:01 raspberrypi python[4571]: Reacting to changed context
Aug 23 23:10:01 raspberrypi python[4571]: Changing source to AUX
Aug 23 23:10:02 raspberrypi python[4571]: CompletedProcess(args=['ir-ctl', '-d', '/dev/lirc0', '-S', 'necx:0x856a24'], returncode=0, stdout=b'', stderr=b'')
  

Однако исходный код не обновляется на моем усилителе. Это меня очень смущает, почему это работает в 100% раз при запуске непосредственно в оболочке и не работает при запуске из python?

Стоит упомянуть, что чередование этих двух:

     #result = subprocess.run(["ir-ctl", "-d","/dev/lirc0","-S", necCommand], capture_output=True, check=True)
result = subprocess.run(['ir-ctl -d /dev/lirc0 -S necx:{}'.format(code)], shell=True, capture_output=True, check=True)
  

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

Я запускаю свой скрипт на virtualenv, Python 3.7.3.

Комментарии:

1. Я был бы более склонен винить ir-ctl , если у вас есть журналы с указанием нулевого кода выхода. Возможно, добавьте какой-нибудь код для повторного запроса, если /dev/lirc0 действительно установлено новое значение. Если это неправильная переменная, попробуйте установить ее снова.

2. Я не уверен, возможно ли это. Устройство позволяет мне только передавать, могу ли я каким-то образом прочитать из файла устройства? У меня не так уж много знаний в этой области. От ir-ctl -f -d /dev/lirc0 : Функции приема / dev /lirc0: — Устройство не может получить функции отправки / dev / lirc0: — Устройство может отправлять необработанный IR — кодировщик ИК-сканирования — Установить несущую — Установить рабочий цикл. ——- Кроме того, возможно ли, что использование virtualenv (и, следовательно, python 3 вместо системного python 2.7 по умолчанию) что-то нарушает внутри ir-ctl?

3. Хм, инструмент, похоже, написан на C, поэтому, я думаю, вряд ли на него повлияет версия python github.com/cz172638/v4l-utils/blob/master/utils/ir-ctl/ir-ctl.c

4. У вас это работает иногда, но не всегда? есть ли какие-то обстоятельства, при которых, по вашему мнению, это срабатывает чаще, чем у других? Каково соотношение между тем, что работает, и тем, что не работает?

5. В общем, кажется, что это работает в течение нескольких запусков команды, затем она прерывается, затем она снова работает. В зависимости от варианта это примерно 50% времени, когда все в порядке или ниже.

Ответ №1:

Возможно, проблема не в коде Python, а в характере обработки ИК-сигналов вашим приемником (стереоусилителем). Может быть недостаточно отправить только одну команду, и вам нужно отправить несколько одновременно. Вместо

 ir-ctl -d /dev/lirc0 -S necx:0x856a24
  

Отправить

 ir-ctl -d /dev/lirc0 -S necx:0x856a24 -S necx:0x856a24 -S necx:0x856a24
  

Из ir-ctl — инструмента швейцарского типа для обработки необработанного IR и настройки параметров lirc:

-S, —scancode=PROTOCOL:SCANCODE Отправляет IR-scancode в указанном протоколе. Протокол должен соответствовать одному из протоколов, перечисленных ниже, за которым следует двоеточие и номер scancode. Если этот параметр указан несколько раз, отправьте все scancodes по порядку с интервалом в 125 мс между ними.Длину промежутка можно изменить с помощью —gap.