Skip to content

Commit

Permalink
Add shuffle and repeat. Also seek, but doesn't seem to work yet.
Browse files Browse the repository at this point in the history
  • Loading branch information
morosanmihail committed Jan 8, 2025
1 parent 0060fd4 commit 595732f
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 29 deletions.
98 changes: 90 additions & 8 deletions src/homeassistant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ pub struct MediaPlayerMetadata {
pub volume: f64,
pub art_url: String,
pub playing: bool,
pub shuffle: bool,
pub repeat: HALoopStatus,
}

#[derive(Debug)]
pub enum HALoopStatus {
None,
Track,
Playlist,
}

#[derive(Debug)]
Expand All @@ -41,6 +50,10 @@ pub enum HAEvent {
MetadataUpdated(MediaPlayerMetadata),
Next,
Previous,
Volume(f64),
SetShuffle(bool),
SetLoop(HALoopStatus),
Seek(i64),
}

pub fn json_to_metadata(
Expand Down Expand Up @@ -87,6 +100,21 @@ pub fn json_to_metadata(
.as_f64()
.ok_or_eyre("Could not convert Number to f64")?,
playing,
repeat: match metadata
.get("repeat")
.unwrap_or(&json!("off"))
.to_string()
.trim_matches(['\"'])
{
"one" => HALoopStatus::Track,
"all" => HALoopStatus::Playlist,
_ => HALoopStatus::None,
},
shuffle: metadata
.get("shuffle")
.unwrap_or(&json!(false))
.as_bool()
.ok_or_eyre("Could not convert Bool to boolean")?,
})
}

Expand All @@ -100,20 +128,57 @@ impl MediaPlayerState {
}

pub async fn play(&self) -> Result<()> {
self.send_command_to_home_assistant("media_play").await
self.send_command_to_home_assistant("media_play", None)
.await
}

pub async fn pause(&self) -> Result<()> {
self.send_command_to_home_assistant("media_pause").await
self.send_command_to_home_assistant("media_pause", None)
.await
}

pub async fn next(&self) -> Result<()> {
self.send_command_to_home_assistant("media_next_track")
self.send_command_to_home_assistant("media_next_track", None)
.await
}

pub async fn previous(&self) -> Result<()> {
self.send_command_to_home_assistant("media_previous_track")
self.send_command_to_home_assistant("media_previous_track", None)
.await
}

pub async fn set_volume(&self, volume: f64) -> Result<()> {
let mut extras = serde_json::Map::new();
extras.insert("volume_level".to_string(), json!(volume));
self.send_command_to_home_assistant("volume_set", Some(extras))
.await
}

pub async fn set_shuffle(&self, shuffle: bool) -> Result<()> {
let mut extras = serde_json::Map::new();
extras.insert("shuffle".to_string(), json!(shuffle));
self.send_command_to_home_assistant("shuffle_set", Some(extras))
.await
}

pub async fn set_loop(&self, loop_status: HALoopStatus) -> Result<()> {
let mut extras = serde_json::Map::new();
extras.insert(
"repeat".to_string(),
json!(match loop_status {
HALoopStatus::None => "off",
HALoopStatus::Track => "one",
HALoopStatus::Playlist => "all",
}),
);
self.send_command_to_home_assistant("repeat_set", Some(extras))
.await
}

pub async fn set_seek(&self, position: i64) -> Result<()> {
let mut extras = serde_json::Map::new();
extras.insert("seek_position".to_string(), json!(position));
self.send_command_to_home_assistant("media_seek", Some(extras))
.await
}

Expand Down Expand Up @@ -143,12 +208,25 @@ impl MediaPlayerState {
Ok(events)
}

pub async fn send_command_to_home_assistant(&self, command: &str) -> Result<()> {
pub async fn send_command_to_home_assistant(
&self,
command: &str,
extra_params: Option<serde_json::Map<String, Value>>,
) -> Result<()> {
let client = reqwest::Client::new();
let url = format!("{}/api/services/media_player/{}", self.ha_url, command);
let params = serde_json::json!({
"entity_id": self.entity_id,
});

let mut params = serde_json::Map::new();
params.insert(
"entity_id".to_string(),
Value::String(self.entity_id.clone()),
);

if let Some(extras) = extra_params {
for (k, v) in extras {
params.insert(k, v);
}
}

client
.post(url)
Expand Down Expand Up @@ -259,6 +337,10 @@ pub async fn listen_for_events(
HAEvent::Pause=> mp.pause().await?,
HAEvent::Next => mp.next().await?,
HAEvent::Previous => mp.previous().await?,
HAEvent::Volume(v) => mp.set_volume(v).await?,
HAEvent::SetShuffle(s) => mp.set_shuffle(s).await?,
HAEvent::SetLoop(l) => mp.set_loop(l).await?,
HAEvent::Seek(p) => mp.set_seek(p).await?,
_ => {},
};
}
Expand Down
4 changes: 1 addition & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,7 @@ async fn main() -> Result<()> {
mpris_rx,
));

while (set.join_next().await).is_some() {
println!("------ GOT SOME RESULT");
}
while (set.join_next().await).is_some() {}
Ok(())
}

Expand Down
80 changes: 62 additions & 18 deletions src/mpris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use tokio::sync::{
Mutex,
};

use crate::homeassistant::{json_to_metadata, HAEvent, MediaPlayer, MediaPlayerMetadata};
use crate::homeassistant::{
json_to_metadata, HAEvent, HALoopStatus, MediaPlayer, MediaPlayerMetadata,
};

#[derive(Clone)]
pub struct MyPlayer {
Expand Down Expand Up @@ -96,13 +98,23 @@ impl PlayerInterface for MyPlayer {
async fn play_pause(&self) -> fdo::Result<()> {
let _ = self
.ha_sender
.send((self.entity_id.clone(), HAEvent::Play))
.send((
self.entity_id.clone(),
if self.metadata.lock().await.playing {
HAEvent::Pause
} else {
HAEvent::Play
},
))
.await;
Ok(())
}

async fn stop(&self) -> fdo::Result<()> {
// TODO
let _ = self
.ha_sender
.send((self.entity_id.clone(), HAEvent::Pause))
.await;
Ok(())
}

Expand All @@ -114,11 +126,19 @@ impl PlayerInterface for MyPlayer {
Ok(())
}

async fn seek(&self, _offset: Time) -> fdo::Result<()> {
async fn seek(&self, offset: Time) -> fdo::Result<()> {
let _ = self
.ha_sender
.send((self.entity_id.clone(), HAEvent::Seek(offset.as_secs())))
.await;
Ok(())
}

async fn set_position(&self, _track_id: TrackId, _position: Time) -> fdo::Result<()> {
async fn set_position(&self, _track_id: TrackId, position: Time) -> fdo::Result<()> {
let _ = self
.ha_sender
.send((self.entity_id.clone(), HAEvent::Seek(position.as_secs())))
.await;
Ok(())
}

Expand All @@ -135,10 +155,26 @@ impl PlayerInterface for MyPlayer {
}

async fn loop_status(&self) -> fdo::Result<LoopStatus> {
Ok(LoopStatus::None)
Ok(match self.metadata.lock().await.repeat {
HALoopStatus::None => LoopStatus::None,
HALoopStatus::Track => LoopStatus::Track,
HALoopStatus::Playlist => LoopStatus::Playlist,
})
}

async fn set_loop_status(&self, _loop_status: LoopStatus) -> mpris_server::zbus::Result<()> {
async fn set_loop_status(&self, loop_status: LoopStatus) -> mpris_server::zbus::Result<()> {
let _ = self
.ha_sender
.send((
self.entity_id.clone(),
HAEvent::SetLoop(match loop_status {
LoopStatus::None => HALoopStatus::None,
LoopStatus::Track => HALoopStatus::Track,
LoopStatus::Playlist => HALoopStatus::Playlist,
}),
))
.await;

Ok(())
}

Expand All @@ -151,10 +187,14 @@ impl PlayerInterface for MyPlayer {
}

async fn shuffle(&self) -> fdo::Result<bool> {
Ok(false)
Ok(self.metadata.lock().await.shuffle)
}

async fn set_shuffle(&self, _shuffle: bool) -> mpris_server::zbus::Result<()> {
async fn set_shuffle(&self, shuffle: bool) -> mpris_server::zbus::Result<()> {
let _ = self
.ha_sender
.send((self.entity_id.clone(), HAEvent::SetShuffle(shuffle)))
.await;
Ok(())
}

Expand All @@ -172,8 +212,11 @@ impl PlayerInterface for MyPlayer {
Ok(self.metadata.lock().await.volume)
}

async fn set_volume(&self, _volume: Volume) -> mpris_server::zbus::Result<()> {
// TODO
async fn set_volume(&self, volume: Volume) -> mpris_server::zbus::Result<()> {
let _ = self
.ha_sender
.send((self.entity_id.clone(), HAEvent::Volume(volume)))
.await;
Ok(())
}

Expand Down Expand Up @@ -206,7 +249,7 @@ impl PlayerInterface for MyPlayer {
}

async fn can_seek(&self) -> fdo::Result<bool> {
Ok(false)
Ok(true)
}

async fn can_control(&self) -> fdo::Result<bool> {
Expand Down Expand Up @@ -251,7 +294,6 @@ pub async fn new_mpris_player(
HAEvent::MetadataUpdated(metadata_update) => {
player
.properties_changed([
Property::CanSeek(false),
Property::Metadata(
Metadata::builder()
.title(metadata_update.title)
Expand All @@ -262,17 +304,19 @@ pub async fn new_mpris_player(
)
.build(),
),
Property::CanSeek(true),
Property::LoopStatus(match metadata_update.repeat {
HALoopStatus::None => LoopStatus::None,
HALoopStatus::Playlist => LoopStatus::Playlist,
HALoopStatus::Track => LoopStatus::Track,
}),
Property::Shuffle(metadata_update.shuffle),
])
.await?;
{
let mut metadata = metadata_lock.lock().await;
metadata.position = metadata_update.position;
}
player
.emit(mpris_server::Signal::Seeked {
position: Time::from_secs(metadata_update.position),
})
.await?;
}
_ => {}
}
Expand Down

0 comments on commit 595732f

Please sign in to comment.