Skip to content

Commit 8ad5dbe

Browse files
committed
Sonos discovery
1 parent f69410d commit 8ad5dbe

File tree

4 files changed

+484
-0
lines changed

4 files changed

+484
-0
lines changed

src/adapters/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ mod ip_camera;
1818
/// An adapter dedicated to the Philips Hue
1919
mod philips_hue;
2020

21+
/// An adapter for Sonos speakers.
22+
mod sonos;
23+
2124
/// An adapter providing access to Thinkerbell.
2225
mod thinkerbell;
2326

@@ -68,6 +71,7 @@ impl<T: Controller> AdapterManager<T> {
6871
let profile_openzwave = &self.controller.get_profile().path_for("openzwave");
6972
let openzwave_device = self.controller.clone().get_config().get("openzwave", "device");
7073
OpenzwaveAdapter::init(manager, profile_openzwave, openzwave_device).unwrap();
74+
sonos::SonosAdapter::init(manager, self.controller.clone()).unwrap();
7175

7276
self.start_tts(manager);
7377
}

src/adapters/sonos/mod.rs

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
//! An adapter providing access to IP cameras. Currently only the following IP cameras are
6+
//! supported: DLink DCS-5010L, DLink DCS-5020L and DLink DCS-5025.
7+
//!
8+
9+
extern crate serde_json;
10+
11+
mod upnp_listener;
12+
mod sonos;
13+
14+
use config_store::ConfigService;
15+
use foxbox_taxonomy::api::{Error, InternalError, User};
16+
use foxbox_taxonomy::manager::*;
17+
use foxbox_taxonomy::selector::*;
18+
use foxbox_taxonomy::services::*;
19+
use foxbox_taxonomy::values::{ Value, Json, Binary, Type, TypeError};
20+
use traits::Controller;
21+
use transformable_channels::mpsc::*;
22+
use self::upnp_listener::SonosUpnpListener;
23+
use self::sonos::*;
24+
use std::collections::{HashMap, HashSet};
25+
use std::sync::{Arc, Mutex};
26+
27+
const CUSTOM_PROPERTY_MANUFACTURER: &'static str = "manufacturer";
28+
const CUSTOM_PROPERTY_MODEL: &'static str = "model";
29+
const CUSTOM_PROPERTY_NAME: &'static str = "name";
30+
const CUSTOM_PROPERTY_URL: &'static str = "url";
31+
const CUSTOM_PROPERTY_UDN: &'static str = "udn";
32+
33+
static ADAPTER_NAME: &'static str = "Sonos adapter";
34+
static ADAPTER_VENDOR: &'static str = "[email protected]";
35+
static ADAPTER_VERSION: [u32; 4] = [0, 0, 0, 0];
36+
37+
pub type SonosServiceMap = Arc<Mutex<SonosServiceMapInternal>>;
38+
39+
pub struct SonosServiceMapInternal {
40+
getters: HashMap<Id<Getter>, Arc<Sonos>>,
41+
setters: HashMap<Id<Setter>, Arc<Sonos>>,
42+
}
43+
44+
pub struct SonosAdapter {
45+
services: SonosServiceMap,
46+
}
47+
48+
impl SonosAdapter {
49+
pub fn id() -> Id<AdapterId> {
50+
Id::new("[email protected]")
51+
}
52+
53+
pub fn init<C>(adapt: &Arc<AdapterManager>, controller: C) -> Result<(), Error>
54+
where C: Controller
55+
{
56+
let services = Arc::new(Mutex::new(SonosServiceMapInternal {
57+
getters: HashMap::new(),
58+
setters: HashMap::new(),
59+
}));
60+
let sonos_adapter = Arc::new(SonosAdapter {
61+
services: services.clone(),
62+
});
63+
64+
try!(adapt.add_adapter(sonos_adapter));
65+
66+
// The UPNP listener will add camera service for discovered speakers.
67+
let upnp = controller.get_upnp_manager();
68+
let listener = SonosUpnpListener::new(adapt, services, &controller.get_config());
69+
upnp.add_listener("SonosTaxonomy".to_owned(), listener);
70+
71+
// Search for Sonos devices.
72+
upnp.search(Some("urn:schemas-upnp-org:device:ZonePlayer:1".to_owned())).unwrap();
73+
Ok(())
74+
}
75+
76+
pub fn init_service(adapt: &Arc<AdapterManager>,
77+
services: SonosServiceMap,
78+
config: &Arc<ConfigService>,
79+
udn: &str,
80+
url: &str,
81+
name: &str,
82+
manufacturer: &str,
83+
model_name: &str) -> Result<(), Error>
84+
{
85+
let service_id = create_service_id(udn);
86+
87+
let adapter_id = Self::id();
88+
let mut service = Service::empty(service_id.clone(), adapter_id.clone());
89+
90+
service.properties.insert(CUSTOM_PROPERTY_MANUFACTURER.to_owned(),
91+
manufacturer.to_owned());
92+
service.properties.insert(CUSTOM_PROPERTY_MODEL.to_owned(), model_name.to_owned());
93+
service.properties.insert(CUSTOM_PROPERTY_NAME.to_owned(), name.to_owned());
94+
service.properties.insert(CUSTOM_PROPERTY_URL.to_owned(), url.to_owned());
95+
service.properties.insert(CUSTOM_PROPERTY_UDN.to_owned(), udn.to_owned());
96+
service.tags.insert(tag_id!(&format!("name:{}", name)));
97+
98+
// Since the upnp_discover will be called about once very 3 minutes we want to ignore
99+
// discoveries if the sonos is already registered.
100+
if let Err(error) = adapt.add_service(service) {
101+
if let Error::InternalError(ref internal_error) = error {
102+
if let InternalError::DuplicateService(_) = *internal_error {
103+
debug!("Found {} @ {} UDN {} (ignoring since it already exists)",
104+
model_name,
105+
url,
106+
udn);
107+
return Ok(());
108+
}
109+
}
110+
111+
panic!(error);
112+
}
113+
114+
info!("Adding Sonos {} Manufacturer: {} Model: {} Name: {} Url: {}",
115+
udn,
116+
manufacturer,
117+
model_name,
118+
name,
119+
url);
120+
121+
/*let getter_image_list_id = create_getter_id("image_list", udn);
122+
try!(adapt.add_getter(Channel {
123+
tags: HashSet::new(),
124+
adapter: adapter_id.clone(),
125+
id: getter_image_list_id.clone(),
126+
last_seen: None,
127+
service: service_id.clone(),
128+
mechanism: Getter {
129+
kind: ChannelKind::Extension {
130+
vendor: Id::new("[email protected]"),
131+
adapter: Id::new("IPCam Adapter"),
132+
kind: Id::new("image_list"),
133+
typ: Type::Json,
134+
},
135+
updated: None,
136+
},
137+
}));
138+
139+
let getter_image_newest_id = create_getter_id("image_newest", udn);
140+
try!(adapt.add_getter(Channel {
141+
tags: HashSet::new(),
142+
adapter: adapter_id.clone(),
143+
id: getter_image_newest_id.clone(),
144+
last_seen: None,
145+
service: service_id.clone(),
146+
mechanism: Getter {
147+
kind: ChannelKind::Extension {
148+
vendor: Id::new("[email protected]"),
149+
adapter: Id::new("IPCam Adapter"),
150+
kind: Id::new("latest image"),
151+
typ: Type::Binary,
152+
},
153+
updated: None,
154+
},
155+
}));
156+
157+
let setter_snapshot_id = create_setter_id("snapshot", udn);
158+
try!(adapt.add_setter(Channel {
159+
tags: HashSet::new(),
160+
adapter: adapter_id.clone(),
161+
id: setter_snapshot_id.clone(),
162+
last_seen: None,
163+
service: service_id.clone(),
164+
mechanism: Setter {
165+
kind: ChannelKind::TakeSnapshot,
166+
updated: None,
167+
},
168+
}));
169+
170+
let getter_username_id = create_getter_id("username", udn);
171+
try!(adapt.add_getter(Channel {
172+
tags: HashSet::new(),
173+
adapter: adapter_id.clone(),
174+
id: getter_username_id.clone(),
175+
last_seen: None,
176+
service: service_id.clone(),
177+
mechanism: Getter {
178+
kind: ChannelKind::Username,
179+
updated: None,
180+
},
181+
}));
182+
183+
let setter_username_id = create_setter_id("username", udn);
184+
try!(adapt.add_setter(Channel {
185+
tags: HashSet::new(),
186+
adapter: adapter_id.clone(),
187+
id: setter_username_id.clone(),
188+
last_seen: None,
189+
service: service_id.clone(),
190+
mechanism: Setter {
191+
kind: ChannelKind::Username,
192+
updated: None,
193+
},
194+
}));
195+
196+
let getter_password_id = create_getter_id("password", udn);
197+
try!(adapt.add_getter(Channel {
198+
tags: HashSet::new(),
199+
adapter: adapter_id.clone(),
200+
id: getter_password_id.clone(),
201+
last_seen: None,
202+
service: service_id.clone(),
203+
mechanism: Getter {
204+
kind: ChannelKind::Password,
205+
updated: None,
206+
},
207+
}));
208+
209+
let setter_password_id = create_setter_id("password", udn);
210+
try!(adapt.add_setter(Channel {
211+
tags: HashSet::new(),
212+
adapter: adapter_id.clone(),
213+
id: setter_password_id.clone(),
214+
last_seen: None,
215+
service: service_id.clone(),
216+
mechanism: Setter {
217+
kind: ChannelKind::Password,
218+
updated: None,
219+
},
220+
}));*/
221+
222+
let mut serv = services.lock().unwrap();
223+
let sonos = Arc::new(Sonos::new(udn, url, name, config));
224+
/*serv.getters.insert(getter_image_list_id, camera.clone());
225+
serv.getters.insert(getter_image_newest_id, camera.clone());
226+
serv.setters.insert(setter_snapshot_id, camera.clone());
227+
serv.getters.insert(getter_username_id, camera.clone());
228+
serv.setters.insert(setter_username_id, camera.clone());
229+
serv.getters.insert(getter_password_id, camera.clone());
230+
serv.setters.insert(setter_password_id, camera.clone());*/
231+
232+
Ok(())
233+
}
234+
}
235+
236+
impl Adapter for SonosAdapter {
237+
fn id(&self) -> Id<AdapterId> {
238+
Self::id()
239+
}
240+
241+
fn name(&self) -> &str {
242+
ADAPTER_NAME
243+
}
244+
245+
fn vendor(&self) -> &str {
246+
ADAPTER_VENDOR
247+
}
248+
249+
fn version(&self) -> &[u32; 4] {
250+
&ADAPTER_VERSION
251+
}
252+
253+
fn fetch_values(&self,
254+
mut set: Vec<Id<Getter>>,
255+
_: User)
256+
-> ResultMap<Id<Getter>, Option<Value>, Error> {
257+
set.drain(..).map(|id| {
258+
let device = match self.services.lock().unwrap().getters.get(&id) {
259+
Some(device) => device.clone(),
260+
None => return (id.clone(), Err(Error::InternalError(InternalError::NoSuchGetter(id))))
261+
};
262+
263+
/*if id == camera.get_username_id {
264+
let rsp = camera.get_username();
265+
return (id, Ok(Some(Value::String(Arc::new(rsp)))));
266+
}
267+
268+
if id == camera.get_password_id {
269+
let rsp = camera.get_password();
270+
return (id, Ok(Some(Value::String(Arc::new(rsp)))));
271+
}
272+
273+
if id == camera.image_list_id {
274+
let rsp = camera.get_image_list();
275+
return (id, Ok(Some(Value::Json(Arc::new(Json(serde_json::to_value(&rsp)))))));
276+
}
277+
278+
if id == camera.image_newest_id {
279+
return match camera.get_newest_image() {
280+
Ok(rsp) => (id, Ok(Some(Value::Binary(Binary {
281+
data: Arc::new(rsp),
282+
mimetype: Id::new("image/jpeg")
283+
})))),
284+
Err(err) => (id, Err(err))
285+
};
286+
}*/
287+
288+
(id.clone(), Err(Error::InternalError(InternalError::NoSuchGetter(id))))
289+
}).collect()
290+
}
291+
292+
fn send_values(&self, mut values: HashMap<Id<Setter>, Value>, _: User) -> ResultMap<Id<Setter>, (), Error> {
293+
values.drain().map(|(id, value)| {
294+
let device = match self.services.lock().unwrap().setters.get(&id) {
295+
Some(device) => device.clone(),
296+
None => { return (id, Err(Error::InternalError(InternalError::InvalidInitialService))); }
297+
};
298+
299+
/*if id == camera.set_username_id {
300+
if let Value::String(ref username) = value {
301+
camera.set_username(username);
302+
return (id, Ok(()));
303+
}
304+
return (id, Err(Error::TypeError(TypeError {
305+
got:value.get_type(),
306+
expected: Type::String
307+
})))
308+
}
309+
310+
if id == camera.set_password_id {
311+
if let Value::String(ref password) = value {
312+
camera.set_password(password);
313+
return (id, Ok(()));
314+
}
315+
return (id, Err(Error::TypeError(TypeError {
316+
got:value.get_type(),
317+
expected: Type::String
318+
})))
319+
}
320+
321+
if id == camera.snapshot_id {
322+
return match camera.take_snapshot() {
323+
Ok(_) => (id, Ok(())),
324+
Err(err) => (id, Err(err))
325+
};
326+
}*/
327+
328+
(id.clone(), Err(Error::InternalError(InternalError::NoSuchSetter(id))))
329+
}).collect()
330+
}
331+
332+
fn register_watch(&self, mut watch: Vec<WatchTarget>) -> WatchResult
333+
{
334+
watch.drain(..).map(|(id, _, _)| {
335+
(id.clone(), Err(Error::GetterDoesNotSupportWatching(id)))
336+
}).collect()
337+
}
338+
}

0 commit comments

Comments
 (0)