appium_client/commands/
battery.rs1use std::collections::HashMap;
3use std::marker::PhantomData;
4use std::ops::Deref;
5use async_trait::async_trait;
6use fantoccini::error::CmdError;
7use serde_json::Value;
8use crate::{AndroidClient, AppiumClientTrait, IOSClient};
9use crate::capabilities::android::AndroidCapabilities;
10use crate::capabilities::AppiumCapability;
11use crate::capabilities::ios::IOSCapabilities;
12
13#[async_trait]
15pub trait HasBattery<Caps>: AppiumClientTrait
16 where Caps: AppiumCapability
17{
18 async fn battery_info(&self) -> Result<BatteryInfo<Caps>, CmdError> {
19 let value = self.execute("mobile: batteryInfo", vec![]).await?;
20 Ok(BatteryInfo {
21 inner: serde_json::from_value(value)?,
22 caps: PhantomData,
23 })
24 }
25}
26
27
28#[async_trait]
29impl HasBattery<AndroidCapabilities> for AndroidClient {}
30
31#[async_trait]
32impl HasBattery<IOSCapabilities> for IOSClient {}
33
34pub struct BatteryInfo<Caps>
35 where Caps: AppiumCapability {
36 inner: HashMap<String, Value>,
37 caps: PhantomData<Caps>,
38}
39
40impl<Caps> BatteryInfo<Caps>
41 where Caps: AppiumCapability {
42
43 pub fn level(&self) -> f64 {
44 self.get("level")
45 .cloned()
46 .and_then(|v| serde_json::from_value(v).ok())
47 .unwrap_or(0f64)
48 }
49}
50
51impl<Caps> Deref for BatteryInfo<Caps>
52 where Caps: AppiumCapability {
53 type Target = HashMap<String, Value>;
54
55 fn deref(&self) -> &Self::Target {
56 &self.inner
57 }
58}
59
60pub trait CanBeCharged {
61 fn is_full(&self) -> bool;
62 fn is_charging(&self) -> bool;
63 fn is_plugged(&self) -> bool;
64 fn is_invalid(&self) -> bool;
65}
66
67#[derive(Copy, Clone, Debug, Eq, PartialEq)]
68pub enum AndroidBatteryState {
69 Unknown,
70 Charging,
71 Discharging,
72 NotCharging,
73 Full,
74}
75
76impl BatteryInfo<AndroidCapabilities> {
77 pub fn state(&self) -> AndroidBatteryState {
78 self.get("state")
79 .cloned()
80 .and_then(|v| serde_json::from_value::<u32>(v).ok())
81 .map(|state| match state {
82 2 => AndroidBatteryState::Charging,
83 3 => AndroidBatteryState::Discharging,
84 4 => AndroidBatteryState::NotCharging,
85 5 => AndroidBatteryState::Full,
86 _ => AndroidBatteryState::Unknown
87 })
88 .unwrap_or(AndroidBatteryState::Unknown)
89 }
90}
91
92impl CanBeCharged for BatteryInfo<AndroidCapabilities> {
93 fn is_full(&self) -> bool {
94 self.state() == AndroidBatteryState::Full
95 }
96
97 fn is_charging(&self) -> bool {
98 self.state() == AndroidBatteryState::Charging
99 }
100
101 fn is_plugged(&self) -> bool {
102 let state = self.state();
103 state == AndroidBatteryState::NotCharging || state == AndroidBatteryState::Discharging
104 }
105
106 fn is_invalid(&self) -> bool {
107 self.state() == AndroidBatteryState::Unknown
108 }
109}
110
111#[derive(Copy, Clone, Debug, Eq, PartialEq)]
112pub enum IOSBatteryState {
113 Unknown,
114 Unplugged,
115 Charging,
116 Full,
117}
118
119impl BatteryInfo<IOSCapabilities> {
120 pub fn state(&self) -> IOSBatteryState {
121 self.get("state")
122 .cloned()
123 .and_then(|v| serde_json::from_value::<u32>(v).ok())
124 .map(|state| match state {
125 1 => IOSBatteryState::Unplugged,
126 2 => IOSBatteryState::Charging,
127 3 => IOSBatteryState::Full,
128 _ => IOSBatteryState::Unknown
129 })
130 .unwrap_or(IOSBatteryState::Unknown)
131 }
132}
133
134impl CanBeCharged for BatteryInfo<IOSCapabilities> {
135 fn is_full(&self) -> bool {
136 self.state() == IOSBatteryState::Full
137 }
138
139 fn is_charging(&self) -> bool {
140 self.state() == IOSBatteryState::Charging
141 }
142
143 fn is_plugged(&self) -> bool {
144 self.state() != IOSBatteryState::Unplugged
145 }
146
147 fn is_invalid(&self) -> bool {
148 self.state() == IOSBatteryState::Unknown
149 }
150}