PythonでAndroidスマホの各種センサー情報を取得する。

# はじめに
PythonでAndroidアプリが作れるなら[ <記事：PythonでAndroidアプリを作る。> ](https://qiita.com/tapitapi/items/e91d67d59ed10caa59f6)、スマホの各種センサー情報を使ってアプリが作れないか？調べてみた。Plyerというkivy用のAPIで各種センサー情報を取得できることがわかったので記事にする。

Plyerはこちら

https://github.com/kivy/plyer

# 目次
+ [Plyerとは](#plyerとは)
+ [Plyerで各種センサー情報を取得する](#plyerで各種センサー情報を取得する)
+ [PlyerのAndroidアプリ例](#plyerのandroidアプリ例)

## Plyerとは
Kivyのフレームワーク上で動作するAPI。OSごとにできることに違いがあり、Androidのサポートは多い印象。この記事では動作確認の取れた〇を中心にまとめる。GPSも試してみたが、Pydroid3ではうまくいくけど、buildozerでアプリ化すると動作不安定になるため△とした。原因がわかり次第、別途記事にする。
センサー以外にも、ライトを光らせる、カメラを動作させる、画面の明るさを変更する、バイブレーションを動かす。など色々できるようだが、また別の機会で記事にしたいと思う。


API          |内容|Android|iOS    |Win|OS X   |Linux
-------------|----|-------|-------|-------|-------|-----
Accelerometer|加速度センサー|〇||✔|✔|✔
Gravity      |重力センサー|〇|✔			
Gyroscope    |ジャイロセンサー|〇|✔	
Compass	     |電子コンパス|〇|✔	
Battery	     |バッテリー情報|〇|✔|✔|✔|✔
Temperature  |温度センサー|〇
Light        |照度センサー|〇
Barometer    |気圧センサー|〇|✔				
GPS          |位置情報|△|✔			

[戻る](#目次)

## Plyerで各種センサー情報を取得する
各々のAPIの使い方をまとめる。```enable()```して値を読むだけなので、比較的簡単にAPIを使うことができる。注意点としてはkivyフレームワーク上でしか動作しないので、plyerを単独で使うことはできない。値が取れない場合は```None```が返答される。

### Accelerometer(加速度センサー)
加速度をx,y,z軸で取得する。```accelerometer.acceleration```でタプル型で取得。
```python:Accelerometer
from plyer import accelerometer
#有効
accelerometer.enable()
#取得
x,y,z = accelerometer.acceleration

if x!=None or y!=None or z!=None:
	output=f'Accelemeter\nx:{x:0.3f} m/s2\ny:{y:0.3f} m/s2\nz:{z:0.3f} m/s2\n\n'
else:
	output=f'Accelemeter\nx: m/s2\ny: m/s2\nz: m/s2\n\n'
```

### Gravity(重力センサー)
重力加速度をx,y,z軸で取得する。```gravity.gravity```でタプル型で取得。
```python:Gravity
from plyer import gravity
#有効
gravity.enable()
#取得
x,y,z = gravity.gravity

if x!=None or y!=None or z!=None:
	output+=f'Gravity\nx:{x:0.3f} m/s2\ny:{y:0.3f} m/s2\nz:{z:0.3f} m/s2\n\n'
else:
	output=f'Gravity\nx: m/s2\ny: m/s2\nz: m/s2\n\n'
```

### Gyroscope(ジャイロセンサー)		
角速度をx,y,z軸で取得する。```gyroscope.rotation```でタプル型で取得。
```python:Gyroscope
from plyer import gyroscope
#有効
gyroscope.enable()
#取得
x,y,z = gyroscope.rotation

if x!=None or y!=None or z!=None:
	output+=f'Gyroscope\nx:{x:0.3f} rad/s\ny:{y:0.3f} rad/s\nz:{z:0.3f} rad/s\n\n'
else:
	output=f'Gyroscope\nx: rad/s\ny: rad/s\nz: rad/s\n'
```

### Compass(電子コンパス)
磁力をx,y,z軸で取得する。```compass.field```でタプル型で取得。
```python:Compass
from plyer import compass
#有効
compass.enable()
#取得
x,y,z = compass.field

if x!=None or y!=None or z!=None:
	output+=f'Compass\nx:{x:0.3f} uT\ny:{y:0.3f} uT\nz:{z:0.3f} uT\n\n'
else:
	output=f'Compass\nx: uT\ny: uT\nz: uT\n\n'
```

### Battery(バッテリー情報)
バッテリーの残量と、現在充電中かどうかを取得する。```battery.status```で辞書型で取得。```isCharging```が充電中かどうか。```percentage```がバッテリー残量。
```python:Battery
from plyer import battery
#batteryはenable()不要。
#取得
output+=f'Battery\nCharging:{battery.status["isCharging"]}\n{battery.status["percentage"]}%\n\n'
```

### Temperature(温度センサー)
温度を取得する。```temperature.temperature```で取得。私のスマホは、常にNoneが返答されたので、スマホ自体が対応していないのだと思う。
```python:Temperature
from plyer import temperature
#有効
temperature.enable()
#取得
output+=f'Temperature\n{temperature.temperature}\n\n'
```

### Light(照度センサー)				
明るさを取得する。```light.illumination```で取得。
```python:Light
from plyer import light
#有効
light.enable()
#取得
output+=f'illumination:\n{light.illumination:0.3f} lux\n\n'
```

### Barometer(気圧センサー)	
気圧を取得する。```barometer.pressure```で取得。
```python:Barometer
from plyer import barometer
#有効
barometer.enable()
#取得
output+=f'Barometer\n{barometer.pressure:0.2f} hPa\n\n'
```

[戻る](#目次)

## PlyerのAndroidアプリ例
KivyのTextInputに1/30秒(スマホが30fpsなので)ごとに値を更新する例を作ってみた。buildozerでアプリ化するための**buildozer.spec**も併せて記載する。**buildozer.spec**は、初期値から変更した行だけ抜き出した。アプリの作り方は [ <記事：PythonでAndroidアプリを作る。> ](https://qiita.com/tapitapi/items/e91d67d59ed10caa59f6) を参照。


<details><summary><b>main.py/buildozer.specファイルはこちら。</summary><div>

```python:main.py
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.clock import Clock

#Sensor
from plyer import accelerometer
from plyer import gravity
from plyer import gyroscope
from plyer import compass
from plyer import battery
from plyer import temperature
from plyer import light
from plyer import barometer
#for compass
import math
import numpy as np

class SensorApp(App):	
	#---------------------------
	# グローバル変数
	#---------------------------
	cnt=0
	toggle='off'

	#---------------------------
	# 初期化
	#---------------------------
	def build(self):
		#TextInput
		self.text = TextInput(text='')
		
		#enable each plyer lib
		accelerometer.enable()
		gravity.enable()
		gyroscope.enable()
		compass.enable()
		temperature.enable()
		light.enable()
		barometer.enable()
		
		# Update every 1/30sec
		Clock.schedule_interval(self.update_plyer, 1/30)
		return self.text
		
	#---------------------------
	# 1/30 sec 毎に更新 InputTextへ出力
	#---------------------------
	def update_plyer(self, dt):
		self.cnt+=1
		
		#accelerometer
		x,y,z = accelerometer.acceleration
		if x!=None or y!=None or z!=None:
			output=f'Accelemeter\nx:{x:0.3f} m/s2\ny:{y:0.3f} m/s2\nz:{z:0.3f} m/s2\n\n'
		else:
			output=f'Accelemeter\nx: m/s2\ny: m/s2\nz: m/s2\n\n'
		
		#gravity
		x,y,z = gravity.gravity
		if x!=None or y!=None or z!=None:
			output+=f'Gravity\nx:{x:0.3f} m/s2\ny:{y:0.3f} m/s2\nz:{z:0.3f} m/s2\n\n'
		else:
			output=f'Gravity\nx: m/s2\ny: m/s2\nz: m/s2\n\n'
		
		#gyroscope
		x,y,z = gyroscope.rotation
		if x!=None or y!=None or z!=None:
			output+=f'Gyroscope\nx:{x:0.3f} rad/s\ny:{y:0.3f} rad/s\nz:{z:0.3f} rad/s\n\n'
		else:
			output=f'Gyroscope\nx: rad/s\ny: rad/s\nz: rad/s\n'
		#compass
		x,y,z = compass.field
		if x!=None or y!=None or z!=None:
			output+=f'Compass\nx:{x:0.3f} uT\ny:{y:0.3f} uT\nz:{z:0.3f} uT\ndegree:{self.orientation(x,y,z):0.3f}\n\n'
		else:
			output=f'Compass\nx: uT\ny: uT\nz: uT\n\n'

		#battery
		output+=f'Battery\nCharging:{battery.status["isCharging"]}\n{battery.status["percentage"]}%\n\n'
		
		#temperature
		output+=f'Temperature\n{temperature.temperature}\n\n'
		
		#light
		if light.illumination!=None:
			output+=f'illumination:\n{light.illumination:0.3f} lux\n\n'
		
		#barometer
		if barometer.pressure!=None:
			output+=f'Barometer\n{barometer.pressure:0.2f} hPa\n\n'

        self.text.text = output

	#---------------------------
	# 方位計算
	#---------------------------
	def orientation(self,x,y,z):
		# ベクトル変換 (x,y逆転しないと90度ずれたため。)
		mag_vector = np.array([y, x, z])		
		# 地磁気ベクトルを計算
		mag_strength = np.linalg.norm(mag_vector)
		mag_unit_vector = mag_vector / mag_strength
		# ラジアン変換
		radian = math.atan2(mag_unit_vector[1], mag_unit_vector[0])
		# 度変換
		degree = math.degrees(radian)
		# 方位0から360度へ調整
		degree = (degree + 360) % 360
		
		return degree

if __name__ == '__main__':
    SensorApp().run()
```

```python:buildozer.spec
[app]

# (str) Title of your application
title = Plyer_test

# (str) Package name
package.name = plyer_test

# (list) Application requirements
requirements = python3, kivy, plyer, android, numpy

# (list) Permissions
android.permissions = INTERNET,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION

```
</div></details>

アプリののスクリーンショット
![Screenshot_20231104-184810.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1299802/6526cdfb-2a79-ad0f-fa3b-06ec48d1f733.png)


[戻る](#目次)

以上