python

Удаленное управление VLC player’ом при помощи Arduino и Python

  • четверг, 7 августа 2014 г. в 03:10:38
http://habrahabr.ru/post/232401/

Добрый день, уважаемые читатели.

Я давно интересовался Arduino, и вот однажды решился на покупку этой замечательной платформы. После недолгих поисков приобрел небольшой Arduino kit, в котором, помимо прочего, был ИК-датчик и пульт к нему. Изучив примеры из мануала, понял, что настало время придумать что-то свое. В итоге я решил сделать удаленное управление VLC player’ом, используя магию Arduino и Python3.

Аrduino магия

Схема подключения и скетч честно взяты без изменений из мануала Arduino kit, скачанного с сайта производителя.



Cкетч
/*
 Arduino Advanced Kit example
 Project 11 - IRRemote
 * IRremote: IRrecvDemo - demonstrates receiving IR codes with IRrecv
 * An IR detector/demodulator must be connected to the input RECV_PIN.
 * Version 0.1 July, 2009
 * Copyright 2009 Ken Shirriff
 * http://arcfn.com
 */
#include <IRremote.h>

int RECV_PIN = 7;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {
    Serial.begin(9600);
    irrecv.enableIRIn(); // Start the receiver
}

void loop() {
    if (irrecv.decode(&results)) {
        Serial.println(results.value,HEX); // print received values
        irrecv.resume(); // Receive the next value
  }
}


В итоге, на монитор порта нам приходят коды нажатых клавиш. Опытным путем было выяснено, что подходят разные ИК-пульты. Хотя это известный факт, но для меня это было в новинку. А вот дальше интереснее. VLC media player имеет удобный web-интерфейс. И управлять им можно посредством обычных http-get запросов вида:

«host:port/page.html?var=value&var2=value2&...»

Более подробную информацию можно найти на videolan.org.

Python3 магия

Используя Python3 и его богатые библиотеки, возможно как посылать http-requests, так и считывать данные с serial port. Этого оказалось достаточно для исполнения моей задумки. Скрипт на Python3 написан с использованием ООП подхода.

Вкратце работа скрипта состоит в следующем:

1. Создаем экземпляр класса vlc_remote_contol, в конструктор передаем адрес нашего Vlc player, список с кодами клавиш, и имя виртуального сom порта.
2. Вызываем метод control в котором происходит считывание с порта кода клавиши.
3. Код клавиши передается в метод __switch_button. где вызывается метод __command с определенными параметрами
4. __command непосредственно осуществляет get запрос к VLC player.

Скрипт
import getpass
import time 
import subprocess
import requests
import serial
import sys
import lxml.etree as etree

class vlc_remote_contol:
    def __init__(self,hst,blist,pname):
        self.host = hst
        self.btn_list = blist
        self.port = serial.Serial(pname, 9600, timeout = 0)
        self.__sess = requests.Session()
        self.__sess.auth = ('',getpass.getpass())
        self.old_vol = self.__get_param(self.__sess.get(self.host+'/requests/status.xml').text, 'volume')
        self.curr_vol = 0
	
    def  __get_param(self,info,param):
        beg = info.find('<'+param+'>')+len('<'+param+'>')
        end = info.find('</'+param+'>')
        return int(info[beg:end])
        
    def  __command (self,host,comm,val):
        if (val == ''):
            req = self.__sess.get(host+'/requests/status.xml'+'?'+'command='+comm)
        else:
            req = self.__sess.get(host+'/requests/status.xml'+'?'+'command='+comm+'&val='+val)
        req.close()
        
    def  __switch_button(self,btn_code):
        if (btn_code == 'FFC23D'):   ## play/pause
            self.__command(self.host,'pl_pause','')
        elif (btn_code == 'FF906F'): ## fullscreen
            self.__command(self.host,'fullscreen','')
        elif (btn_code == 'FFE01F'): ## volume down
            self.__command(self.host,'volume','-10')
        elif (btn_code == 'FFA857'): ## volume up
            self.__command(self.host,'volume','+10')
        elif (btn_code == 'FF22DD'): ## rewind back
            self.__command(self.host,'seek','-0.5%')
        elif (btn_code == 'FF02FD'): ## rewind forward
            self.__command(self.host,'seek','+0.5%')
        elif (btn_code == 'FFA25D'): ## previous track
            self.__command(self.host,'pl_previous','')
        elif (btn_code == 'FFE21D'): ## previous next
            self.__command(self.host,'pl_next','')
        elif (btn_code == 'FF6897'): ## mute
            self.cur_vol = self.__get_param(self.__sess.get(self.host+'/requests/status.xml').text, 'volume')
            if (self.cur_vol  == 0):
                self.__command(self.host,'volume',str(self.old_vol))
            else:
                self.old_vol = self.cur_vol
                self.__command(self.host,'volume','0')
        else:
            pass

    def control(self):
        print('Success')
        while True:
            res = self.port.readline().strip().decode("UTF-8")
            if (res in self.btn_list):
                self.__switch_button(res)
        
def main():
    conf = etree.parse('conf.xml')
    vpath = conf.xpath('/document/vpath/text()')[0]
    vhost = conf.xpath('/document/vhost/text()')[0]
    try:
        subprocess.Popen([vpath])
    except:
        input('VLC player not found')
        return 
    try:
        buttons0 = ['FFA25D','FF629D','FFE21D','FF22DD','FF02FD','FFC23D','FFE01F','FFA857','FF906F','FF6897',
                'FF9867','FFB04F','FF30CF', 'FF18E7','FF7A85','FF10EF','FF38C7','FF5AA5','FF42BD','FF4AB5', 'FF52AD']
        vrc = vlc_remote_contol(vhost,buttons0,"COM3") 
        vrc.control()
    except:
        input("Connection or authorization error.")
        return

if  __name__ ==  "__main__" : 
    main()



Заключение

Я понимаю, что не придумал ничего оригинального и нового. Это мой первый опыт в промышленном программировании, до этого были только лабораторные работы в Университете и решение олимпиадных задач. Жду от сообщества конструктивной критики и советов по улучшению скрипта. Пока в планах добавить коды клавиш в сonf.xml файл, что было использовать другие пульты, и возможно простой графический интерфейс.

Все исходники опубликованы на GitHub.