Add Sonos module (#655)
This commit is contained in:
parent
0d7156b5b1
commit
f6e3b40b24
@ -61,7 +61,8 @@ MOCK_MODULES = [
|
||||
"requests.packages.urllib3.response",
|
||||
"lxml.etree",
|
||||
"requests.adapters",
|
||||
"exchangelib"
|
||||
"exchangelib",
|
||||
"soco"
|
||||
]
|
||||
|
||||
for mod_name in MOCK_MODULES:
|
||||
|
166
i3pystatus/sonos.py
Normal file
166
i3pystatus/sonos.py
Normal file
@ -0,0 +1,166 @@
|
||||
from i3pystatus import IntervalModule
|
||||
import soco
|
||||
|
||||
|
||||
class Sonos(IntervalModule):
|
||||
"""
|
||||
Controls and displays information from Sonos devices.
|
||||
|
||||
A devices is found by IP, by name, or automatically if no IP or name is \
|
||||
supplied.
|
||||
|
||||
.. rubric:: Available formatters
|
||||
|
||||
* `{player_name}` — player name
|
||||
* `{volume}` — volume from 0 to 100
|
||||
* `{muted}` — "M" if muted, else ""
|
||||
* `{title}` — the title of the current song
|
||||
* `{artist}` — the artist of the current song
|
||||
* `{album}` — the album of the current song
|
||||
* `{duration}` — duration of the current song (%M:%S)
|
||||
* `{position}` — position in the current song (%M:%S)
|
||||
* `{state}` — Playing, Paused, Stopped
|
||||
|
||||
Requires: soco (can be installed using pip)
|
||||
"""
|
||||
|
||||
ip = None
|
||||
name = None
|
||||
format = "{state}: {artist} - {title} [{muted}{volume:.0f}%]"
|
||||
color = "#FFFFFF"
|
||||
format_no_music = "No music"
|
||||
color_no_music = "#888888"
|
||||
format_no_connection = "No connection"
|
||||
color_no_connection = "#888888"
|
||||
hide_no_connection = False
|
||||
|
||||
interval = 1
|
||||
|
||||
settings = (
|
||||
("ip", "Speaker IP address."),
|
||||
("name", "Speaker name (used if no IP is given)."),
|
||||
("format", "Format used when playing or paused."),
|
||||
("color", "Color used when playing or paused."),
|
||||
("format_no_music", "Format used when stopped."),
|
||||
("color_no_music", "Color used when stopped."),
|
||||
("format_no_connection", "Format used if no player is connected."),
|
||||
("color_no_connection", "Color used if no player is connected."),
|
||||
("hide_no_connection", "Hide output if no player is connected."),
|
||||
)
|
||||
|
||||
state_text_map = {
|
||||
"PLAYING": "Playing",
|
||||
"TRANSITIONING": "Playing",
|
||||
"PAUSED_PLAYBACK": "Paused",
|
||||
"STOPPED": "Stopped",
|
||||
}
|
||||
|
||||
on_leftclick = "play_pause"
|
||||
on_upscroll = "incr_vol"
|
||||
on_middleclick = "toggle_mute"
|
||||
on_downscroll = "decr_vol"
|
||||
on_doubleleftclick = "next_song"
|
||||
|
||||
player = None
|
||||
|
||||
def run(self):
|
||||
if not self.player:
|
||||
if self.ip:
|
||||
self.player = soco.SoCo(self.ip)
|
||||
elif self.name:
|
||||
self.player = soco.discovery.by_name(self.name)
|
||||
else:
|
||||
self.player = soco.discovery.any_soco()
|
||||
|
||||
if not self.player:
|
||||
self.output = self.output_no_connection
|
||||
return
|
||||
|
||||
try:
|
||||
track_info = self.group_coordinator.get_current_track_info()
|
||||
transp_info = self.group_coordinator.get_current_transport_info()
|
||||
player_name = self.player.player_name
|
||||
muted = self.player.mute
|
||||
volume = self.player.volume
|
||||
except:
|
||||
self.output = self.output_no_connection
|
||||
return
|
||||
|
||||
state = transp_info["current_transport_state"]
|
||||
|
||||
cdict = {
|
||||
"player_name": player_name,
|
||||
"volume": volume,
|
||||
"muted": "M" if muted else "",
|
||||
"title": track_info["title"],
|
||||
"artist": track_info["artist"],
|
||||
"album": track_info["album"],
|
||||
"duration": track_info["duration"][2:],
|
||||
"position": track_info["position"][2:],
|
||||
"state": self.state_text_map[state],
|
||||
}
|
||||
|
||||
self.data = cdict
|
||||
|
||||
if self.format_no_music is not None and state == "STOPPED":
|
||||
self.output = {
|
||||
"full_text": self.format_no_music.format(**cdict),
|
||||
"color": self.color_no_music
|
||||
}
|
||||
else:
|
||||
self.output = {
|
||||
"full_text": self.format.format(**cdict),
|
||||
"color": self.color
|
||||
}
|
||||
|
||||
@property
|
||||
def output_no_connection(self):
|
||||
if self.hide_no_connection:
|
||||
return None
|
||||
else:
|
||||
return {
|
||||
"full_text": self.format_no_connection,
|
||||
"color": self.color_no_connection
|
||||
}
|
||||
|
||||
@property
|
||||
def group_coordinator(self):
|
||||
try:
|
||||
return self.player.group.coordinator
|
||||
except:
|
||||
return None
|
||||
|
||||
def play_pause(self):
|
||||
try:
|
||||
transp_info = self.group_coordinator.get_current_transport_info()
|
||||
state = transp_info["current_transport_state"]
|
||||
if state in ["PLAYING", "TRANSITIONING"]:
|
||||
self.group_coordinator.pause()
|
||||
else:
|
||||
self.group_coordinator.play()
|
||||
except:
|
||||
return
|
||||
|
||||
def incr_vol(self):
|
||||
try:
|
||||
self.player.volume += 1
|
||||
except:
|
||||
return
|
||||
|
||||
def decr_vol(self):
|
||||
try:
|
||||
self.player.volume -= 1
|
||||
except:
|
||||
return
|
||||
|
||||
def toggle_mute(self):
|
||||
try:
|
||||
self.player.mute = not self.player.mute
|
||||
except:
|
||||
return
|
||||
|
||||
def next_song(self):
|
||||
try:
|
||||
self.group_coordinator.next()
|
||||
except:
|
||||
return
|
Loading…
Reference in New Issue
Block a user