1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! GATT client support
16 
17 use crate::wrapper::ClosureCallback;
18 use pyo3::types::PyTuple;
19 use pyo3::{intern, PyObject, PyResult, Python};
20 
21 /// A GATT service on a remote device
22 pub struct ServiceProxy(pub(crate) PyObject);
23 
24 impl ServiceProxy {
25     /// Discover the characteristics in this service.
26     ///
27     /// Populates an internal cache of characteristics in this service.
discover_characteristics(&mut self) -> PyResult<()>28     pub async fn discover_characteristics(&mut self) -> PyResult<()> {
29         Python::with_gil(|py| {
30             self.0
31                 .call_method0(py, intern!(py, "discover_characteristics"))
32                 .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py)))
33         })?
34         .await
35         .map(|_| ())
36     }
37 }
38 
39 /// A GATT characteristic on a remote device
40 pub struct CharacteristicProxy(pub(crate) PyObject);
41 
42 impl CharacteristicProxy {
43     /// Subscribe to changes to the characteristic, executing `callback` for each new value
subscribe( &mut self, callback: impl Fn(Python, &PyTuple) -> PyResult<()> + Send + 'static, ) -> PyResult<()>44     pub async fn subscribe(
45         &mut self,
46         callback: impl Fn(Python, &PyTuple) -> PyResult<()> + Send + 'static,
47     ) -> PyResult<()> {
48         let boxed = ClosureCallback::new(move |py, args, _kwargs| callback(py, args));
49 
50         Python::with_gil(|py| {
51             self.0
52                 .call_method1(py, intern!(py, "subscribe"), (boxed,))
53                 .and_then(|obj| pyo3_asyncio::tokio::into_future(obj.as_ref(py)))
54         })?
55         .await
56         .map(|_| ())
57     }
58 
59     /// Read the current value of the characteristic
read_value(&self) -> PyResult<PyObject>60     pub async fn read_value(&self) -> PyResult<PyObject> {
61         Python::with_gil(|py| {
62             self.0
63                 .call_method0(py, intern!(py, "read_value"))
64                 .and_then(|obj| pyo3_asyncio::tokio::into_future(obj.as_ref(py)))
65         })?
66         .await
67     }
68 }
69 
70 /// Equivalent to the Python `ProfileServiceProxy`.
71 pub trait ProfileServiceProxy {
72     /// The module containing the proxy class
73     const PROXY_CLASS_MODULE: &'static str;
74     /// The module class name
75     const PROXY_CLASS_NAME: &'static str;
76 
77     /// Wrap a PyObject in the Rust wrapper type
wrap(obj: PyObject) -> Self78     fn wrap(obj: PyObject) -> Self;
79 }
80