1use anyhow::{Context as _, Error, format_err};
6use async_trait::async_trait;
7use fidl_fuchsia_input_report::{
8 DeviceDescriptor, InputReportsReaderMarker, InputReportsReaderProxy, SensorInputDescriptor,
9 SensorType, ServiceMarker,
10};
11use fuchsia_component::client::Service;
12
13#[derive(Debug)]
14pub struct AmbientLightInputRpt {
15 pub illuminance: f32,
16 pub red: f32,
17 pub green: f32,
18 pub blue: f32,
19}
20
21struct AmbientLightComponent {
22 pub report_index: usize,
23 pub exponent: i32,
24 pub report_id: u8, }
26
27struct AmbientLightInputReportReaderProxy {
28 pub proxy: InputReportsReaderProxy,
29
30 pub illuminance: Option<AmbientLightComponent>,
31 pub red: Option<AmbientLightComponent>,
32 pub green: Option<AmbientLightComponent>,
33 pub blue: Option<AmbientLightComponent>,
34}
35
36use futures::StreamExt;
37
38async fn open_sensor_input_report_reader() -> Result<AmbientLightInputReportReaderProxy, Error> {
39 let mut watcher = Service::open(ServiceMarker)?
40 .watch()
41 .await
42 .context("Failed to watch for sensor service instances")?;
43
44 while let Some(instance_result) = watcher.next().await {
45 let instance = instance_result?;
46 let device = instance.connect_to_input_device()?;
47
48 fn get_sensor_input(
49 descriptor: &DeviceDescriptor,
50 ) -> Result<&Vec<SensorInputDescriptor>, Error> {
51 let sensor = descriptor.sensor.as_ref().context("device has no sensor")?;
52 let input_desc = sensor.input.as_ref().context("sensor has no input descriptor")?;
53 Ok(input_desc)
54 }
55
56 if let Ok(descriptor) = device.get_descriptor().await {
57 match get_sensor_input(&descriptor) {
58 Ok(input_desc) => {
59 let mut illuminance = None;
60 let mut red = None;
61 let mut green = None;
62 let mut blue = None;
63
64 for input in input_desc {
65 let axes =
66 input.values.as_ref().context("SensorInputDescriptor has no values")?;
67 for (i, val) in axes.iter().enumerate() {
68 let component = AmbientLightComponent {
69 report_index: i,
70 exponent: val.axis.unit.exponent,
71 report_id: input.report_id.unwrap_or(0),
72 };
73 match val.type_ {
74 SensorType::LightIlluminance => illuminance = Some(component),
75 SensorType::LightRed => red = Some(component),
76 SensorType::LightGreen => green = Some(component),
77 SensorType::LightBlue => blue = Some(component),
78 _ => {}
79 }
80 }
81 }
82
83 if illuminance.is_some() {
84 let (proxy, server_end) =
85 fidl::endpoints::create_proxy::<InputReportsReaderMarker>();
86 if let Ok(()) = device.get_input_reports_reader(server_end) {
87 return Ok(AmbientLightInputReportReaderProxy {
88 proxy: proxy,
89 illuminance,
90 red,
91 blue,
92 green,
93 });
94 }
95 }
96 }
97 Err(e) => {
98 log::info!("Skip device {:?}: {}", instance.instance_name(), e);
99 }
100 };
101 }
102 }
103 Err(format_err!("no sensor found"))
104}
105
106async fn read_sensor_input_report(
108 device: &AmbientLightInputReportReaderProxy,
109) -> Result<Option<AmbientLightInputRpt>, Error> {
110 let r = device.proxy.read_input_reports().await;
111
112 match r {
113 Ok(Ok(reports)) => {
114 for report in reports {
115 if report.report_id.unwrap_or(0) != device.illuminance.as_ref().unwrap().report_id {
116 continue;
117 }
118
119 let sensor =
120 report.sensor.context("sensor required in InputReport for sensor device")?;
121 let values = sensor.values.context("values required in SensorInputReport")?;
122 let f = |component: &Option<AmbientLightComponent>| match component {
123 Some(val) => match val.exponent {
124 0 => values[val.report_index] as f32,
125 _ => values[val.report_index] as f32 * f32::powf(10.0, val.exponent as f32),
126 },
127 None => 0.0,
128 };
129
130 let illuminance = f(&device.illuminance);
131 let red = f(&device.red);
132 let green = f(&device.green);
133 let blue = f(&device.blue);
134
135 return Ok(Some(AmbientLightInputRpt { illuminance, red, blue, green }));
136 }
137 Ok(None)
138 }
139 Ok(Err(e)) => Err(format_err!("ReadInputReports error: {}", e)),
140 Err(e) => Err(format_err!("FIDL call failed: {}", e)),
141 }
142}
143
144fn default_report() -> Result<Option<AmbientLightInputRpt>, Error> {
146 Ok(Some(AmbientLightInputRpt { illuminance: 200.0, red: 200.0, green: 200.0, blue: 200.0 }))
147}
148
149pub struct Sensor {
150 proxy: Option<AmbientLightInputReportReaderProxy>,
151}
152
153impl Sensor {
154 pub async fn new() -> Sensor {
155 let proxy = open_sensor_input_report_reader().await;
156 match proxy {
157 Ok(proxy) => return Sensor { proxy: Some(proxy) },
158 Err(_e) => {
159 println!("No valid sensor found.");
160 return Sensor { proxy: None };
161 }
162 }
163 }
164
165 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
166 if self.proxy.is_none() {
167 default_report()
168 } else {
169 read_sensor_input_report(self.proxy.as_ref().unwrap()).await
170 }
171 }
172}
173
174#[async_trait]
175pub trait SensorControl: Send {
176 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error>;
177}
178
179#[async_trait]
180impl SensorControl for Sensor {
181 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
182 self.read().await
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use fuchsia_async as fasync;
190
191 #[fasync::run_singlethreaded(test)]
192 async fn test_open_sensor_error() {
193 let sensor = Sensor { proxy: None };
194 if let Some(ambient_light_input_rpt) = sensor.read().await.unwrap() {
195 assert_eq!(ambient_light_input_rpt.illuminance, 200.0);
196 assert_eq!(ambient_light_input_rpt.red, 200.0);
197 assert_eq!(ambient_light_input_rpt.green, 200.0);
198 assert_eq!(ambient_light_input_rpt.blue, 200.0);
199 }
200 }
201}