Давно возникла потребость озвучивать различные события в разных частях дома. Например в дальних частях дома и на втором этаже не слышно как открывается/закрывается входная дверь или позвать всех домашних к столу что-бы не кричать на весь дом. Или озвучить персональное сообщение в конкретной комнате.
Частичным решением были потолочные динамики в коридорах первого и вторго этажей, кабеля которых шли на правый и левый канал звуковой карты компа на котором стоит IntraHouse. И воспроизведение сообщений происходило за счет проигрывания файлов локально на сервере IntraHouse. Но все же этого не хватало.
После долгих поисков обнаружил что при помощи скриптов можно удаленно управлять плеерами Logitech Media Server(LMS) - нашел скрипт на pearl который делал как раз то что мне было нужно: https://www.domoticz.com/wiki/Logitech_Media_Server но, к сожалению, я так и не смог заставить этот скрипт заработать. А это было бы замечательным решением, т.к. на практически любой линукс или андройд можно поставить SqueezePlayer - клиент для LMS и воспроизводить медиафайлы как на каком-либо отдельном плеере так и синхронно на нескольких. В том числе таким образом можно организовать воспроизведение звука на клиентах где стоит IntraHouse Kiosk.
Как оказалось из разбора того скрипта на pearl - LMS поддерживает протокол JSON-RPC, и плюс еще нашелся API JSON-RPC для LMS: https://gist.github.com/samtherussell/335bf9ba75363bd167d2470b8689d9f2
В результате по мотивам того скрипта написал свой скрипт на python-е. Он приостанавливает воспроизведение тех радиостанций, которые играют в текущий момент на плеерах, сохраняет настройки медиаплееров, формирует синхрогруппу из тех плееров которые должны воспроизвести сообщение, и после сообщения разбирает синхрогруппу, восстанавливает настройки плееров и запускает воспроизведение тех радиостанций которые играли ранее.
скрипт: lms_alert_player.py
#-*- coding: UTF-8 -*-
from sys import argv
import requests
import json
import time
script, alert_file = argv # запоминаем аргументы переданные из коммандной строки - в данном случае - файл для воспроизведения
#script, alert_file, alert_volume = argv
#print alert_file
#print alert_volume
# объявляем переменные
lms_ip = '10.0.0.2:9000' # IP адрес и порт логитеч медиа плеера
player_mac = {'hall': 'd8:cb:8a:9c:67:63', 'kitchen': 'ad:d4:61:6d:65:33', 'parlor': 'dd:bb:cc:dd:00:44'} # словарь потенциальных плееров
# на которых надо будет озвучивать сообщение ключ - произвольный псевдоним плеера, значение - мак адрес плеера
online_players = list() # список экземпляров класса плеер на базе
master_player_mac = "" # переменная в которой будет содержаться мак адрес мастер-плеера
"""---------- процедура для упрощения вызова процедуры протокола JSONRPC --------------------------"""
def callRPC(params):
data = {"jsonrpc":"2.0", "method": "slim.request", "params": params ,'id':1}
httpResponse = requests.post("http://%s/jsonrpc.js" % lms_ip, data=json.dumps(data))
return json.loads(httpResponse.text)
""" Класс для получения и хранения свойств удаленного плеера """
class LMS_Player(object):
def __init__(self, alias, mac):
self.alias = alias
self.mac = mac
try:
answer = callRPC([mac, ["status","-"] ])
self.name = answer["result"]["player_name"]
self.power = answer["result"]["power"]
self.volume = answer["result"]["mixer volume"]
self.repeat = answer["result"]["playlist repeat"]
self.mode = answer["result"]["mode"]
except Exception as e: # в том случае если не получили ответ от плеера (например он сейчас физически выключен)
pass
self.name = ""
self.power = -1
self.volume = -1
self.repeat = -1
self.time = 0
self.mode = ""
try:
self.time = answer["result"]["time"]
except Exception as e: # в том случае если был включен такой режим плеера в которм нет свойства time
pass
self.time = 0
try:
answer = callRPC([mac, ['playlist', 'path', '?'] ])
self.path = answer["result"]["_path"]
except Exception as e: # в том случае если не получили ответ от плеера (например он сейчас физически выключен)
pass
self.path = ""
def print_properties(self): # процедура вывода свойств плеера. Использовалась в процессе отладки
print "*******************************"
print "Alias: %s" % self.alias
print "MAC: %s" % self.mac
print "Name: %s" % self.name
print "Power: %s" % self.power
print "Volume: %s" % self.volume
print "Repeat: %s" % self.repeat
print "Time: %s" % self.time
print "Mode: %s" % self.mode
print "Path: %s" % self.path
# создаем список экземпляров класса LMS_Player в соответсвии с ранее объявленным словарем потенциальных плееров
for i in player_mac.keys():
online_players.append(LMS_Player(i, player_mac.get(i)));
z = range(len(online_players)); z.reverse() # сортируем список экземпляров плееров в обратном порядке в другой список что-бы удобнее было удалять физически отключенные
for i in z:
online_players[i].print_properties()
if (online_players[i].power == - 1): online_players.pop(i); print "^^^^^^ this player was deleted from syncro-list ^^^^^^" # удаляем плееры от котрых не получили ответ
# подготавливаем плееры к воспроизведению нашего сообщения
for current_player in online_players:
if (current_player.mode == "play"): callRPC([current_player.mac, ['pause']])
callRPC([current_player.mac, ['power', '1']])
# callRPC([current_player.mac, ['mixer', 'volume', alert_volume]]) # на тот случай если в будущем захочется принудительно выставлять громкость на плеерах
callRPC([current_player.mac, ['playlist', 'repeat', '0']])
callRPC([current_player.mac, ['playlist', 'clear']])
if (master_player_mac == ""): master_player_mac = current_player.mac; continue # запоминаем мак адрес первого попавшегося плеера
callRPC([master_player_mac, ["sync", current_player.mac]]) # назначаем тот первый попавшийся плеер мастером для синхрогруппы
# воспроизводим наше сообщение
callRPC([master_player_mac, ['playlist', 'clear']])
callRPC([master_player_mac, ['playlist', 'add', alert_file]])
callRPC([master_player_mac, ['play']])
# ждем пока закончится воспроизведение файла нашего сообщения
while True:
answer = callRPC([current_player.mac, ["status","-"]])
time.sleep(0.5)
if (answer["result"]["mode"] != 'play'): break
# восстанавливаем настройки каждого плеера
for current_player in online_players:
callRPC([current_player.mac, ['mixer', 'volume', current_player.volume]])
callRPC([current_player.mac, ["sync","-"]])
callRPC([current_player.mac, ['playlist', 'clear']])
callRPC([current_player.mac, ['playlist', 'repeat', current_player.repeat]])
callRPC([current_player.mac, ['playlist', 'add', current_player.path]])
callRPC([current_player.mac, ['mode', current_player.mode]])
callRPC([current_player.mac, ['time', current_player.time]])
callRPC([current_player.mac, ['power', current_player.power]])
Что бы скрипт заработал в нем прописываем мак-адреса и псевдонимы плееров на которых хотим воспроизводить сообщение. А так же IP адрес и порт LMS. При первом запуске скрипт может ругаться на неустановленные питоновские пакеты, в таком случае доустанавливаем необходимые пакеты при помощи установщика пакетов pip. Далее при запуске скрипта указываем в качестве аргумента путь и имя медиафайла который хотим воспроизвести, при этом тот путь о котором знает и LMS у меня это предопределенная папка "музыка". Для запуска скрипта из среды IntraHouse это выглядит вот так при условии что скрипт называется lms_alert_player.py и находится в папке /media/:
this.execOS(`python /media/lms_alert_player.py /media/go_eat.wav`);