1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use std::{
    sync::{
        mpsc::{self, TryRecvError},
        Arc,
        Mutex,
    },
    thread,
    time::Duration,
};

use libmdns;

use crate::Error;

/// An mDNS Responder. Used to announce the Accessory's name and HAP TXT records to potential
/// controllers.
pub struct Responder {
    name: String,
    port: u16,
    txt_records: [String; 8],
    stop: Option<mpsc::Sender<()>>,
}

impl Responder {
    /// Creates a new mDNS Responder.
    pub fn new(name: &str, port: u16, txt_records: [String; 8]) -> Self {
        Responder {
            name: name.to_string(),
            port,
            txt_records,
            stop: None,
        }
    }

    /// Starts mDNS announcement in a separate thread.
    pub fn start(&mut self) {
        let (tx, rx) = mpsc::channel();
        let name = self.name.clone();
        let port = self.port;
        let tr = self.txt_records.clone();
        thread::spawn(move || {
            let responder = libmdns::Responder::new().expect("couldn't create mDNS responder");
            let _svc = responder.register("_hap._tcp".into(), name, port, &[
                &tr[0], &tr[1], &tr[2], &tr[3], &tr[4], &tr[5], &tr[6], &tr[7],
            ]);
            loop {
                thread::sleep(Duration::from_secs(2));
                match rx.try_recv() {
                    Ok(_) | Err(TryRecvError::Disconnected) => {
                        break;
                    },
                    Err(TryRecvError::Empty) => {},
                }
            }
        });
        self.stop = Some(tx);
    }

    /// Stops mDNS announcement.
    pub fn stop(&self) -> Result<(), Error> {
        if let Some(stop) = self.stop.clone() {
            stop.send(())?;
        }
        Ok(())
    }

    /// Stops mDNS announcement and restarts it with updated TXT records.
    pub fn update_txt_records(&mut self, txt_records: [String; 8]) -> Result<(), Error> {
        self.stop()?;
        self.txt_records = txt_records;
        self.start();
        Ok(())
    }
}

/// Reference counting pointer to a `Responder`.
pub type ResponderPtr = Arc<Mutex<Responder>>;