Add power sensors: Interactive, Doze, Power Save (#885)

* Add power sensors

* Fix state translation to allow the sensors to be binary
This commit is contained in:
Daniel Shokouhi 2020-09-06 05:28:04 -07:00 committed by GitHub
parent aee090144b
commit ba127fd5bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 149 additions and 3 deletions

View file

@ -8,6 +8,7 @@ import android.content.IntentFilter
import android.media.AudioManager import android.media.AudioManager
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import android.os.Build import android.os.Build
import android.os.PowerManager
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import io.homeassistant.companion.android.common.dagger.AppComponent import io.homeassistant.companion.android.common.dagger.AppComponent
import io.homeassistant.companion.android.common.dagger.Graph import io.homeassistant.companion.android.common.dagger.Graph
@ -39,6 +40,24 @@ open class HomeAssistantApplication : Application(), GraphComponentAccessor {
} }
) )
// This will cause interactive and power save to update upon a state change
registerReceiver(
sensorReceiver, IntentFilter().apply {
addAction(Intent.ACTION_SCREEN_OFF)
addAction(Intent.ACTION_SCREEN_ON)
addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
}
)
// Update doze mode immediately on supported devices
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
registerReceiver(
sensorReceiver, IntentFilter().apply {
addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
}
)
}
// This will trigger an update any time the wifi state has changed // This will trigger an update any time the wifi state has changed
registerReceiver( registerReceiver(
sensorReceiver, sensorReceiver,

View file

@ -17,13 +17,15 @@ data class SensorWithAttributes(
val attributes = attributes.map { it.name to it.value }.toMap() val attributes = attributes.map { it.name to it.value }.toMap()
val state = when (sensor.stateType) { val state = when (sensor.stateType) {
"" -> "" "" -> ""
"boolean" -> sensor.state.toBoolean()
"float" -> sensor.state.toFloat()
"int" -> sensor.state.toInt()
"string" -> sensor.state "string" -> sensor.state
"number" -> sensor.state.toFloat()
else -> throw IllegalArgumentException("State is of unknown type: ${sensor.stateType}") else -> throw IllegalArgumentException("State is of unknown type: ${sensor.stateType}")
} }
return SensorRegistration( return SensorRegistration(
sensor.id, sensor.id,
sensor.state, state,
sensor.type, sensor.type,
sensor.icon, sensor.icon,
attributes, attributes,

View file

@ -0,0 +1,115 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import android.content.Context.POWER_SERVICE
import android.os.Build
import android.os.PowerManager
import androidx.annotation.RequiresApi
import io.homeassistant.companion.android.R
class PowerSensorManager : SensorManager {
companion object {
private const val TAG = "PowerSensors"
private const val packageName = "io.homeassistant.companion.android"
private val interactiveDevice = SensorManager.BasicSensor(
"is_interactive",
"binary_sensor",
R.string.basic_sensor_name_interactive,
R.string.sensor_description_interactive
)
private val doze = SensorManager.BasicSensor(
"is_idle",
"binary_sensor",
R.string.basic_sensor_name_doze,
R.string.sensor_description_doze
)
private val powerSave = SensorManager.BasicSensor(
"power_save",
"binary_sensor",
R.string.basic_sensor_name_power_save,
R.string.sensor_description_power_save
)
}
override val enabledByDefault: Boolean
get() = false
override val name: Int
get() = R.string.sensor_name_power
override val availableSensors: List<SensorManager.BasicSensor>
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
listOf(interactiveDevice, doze, powerSave)
} else {
listOf(interactiveDevice, powerSave)
}
override fun requiredPermissions(): Array<String> {
return emptyArray()
}
override fun requestSensorUpdate(
context: Context
) {
val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
updateInteractive(context, powerManager)
updatePowerSave(context, powerManager)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
updateDoze(context, powerManager)
}
}
private fun updateInteractive(context: Context, powerManager: PowerManager) {
if (!isEnabled(context, interactiveDevice.id))
return
val interactiveState = powerManager.isInteractive
val icon = if (interactiveState) "mdi:cellphone" else "mdi:cellphone-off"
onSensorUpdated(
context,
interactiveDevice,
interactiveState,
icon,
mapOf()
)
}
@RequiresApi(Build.VERSION_CODES.M)
private fun updateDoze(context: Context, powerManager: PowerManager) {
if (!isEnabled(context, doze.id))
return
val dozeState = powerManager.isDeviceIdleMode
val icon = if (dozeState) "mdi:sleep" else "mdi:sleep-off"
onSensorUpdated(
context,
doze,
dozeState,
icon,
mapOf(
"ignoring_battery_optizimations" to powerManager.isIgnoringBatteryOptimizations(packageName)
)
)
}
private fun updatePowerSave(context: Context, powerManager: PowerManager) {
if (!isEnabled(context, powerSave.id))
return
val powerSaveState = powerManager.isPowerSaveMode
val icon = "mdi:battery-plus"
onSensorUpdated(
context,
powerSave,
powerSaveState,
icon,
mapOf()
)
}
}

View file

@ -71,8 +71,10 @@ interface SensorManager {
sensor.id = basicSensor.id sensor.id = basicSensor.id
sensor.state = state.toString() sensor.state = state.toString()
sensor.stateType = when (state) { sensor.stateType = when (state) {
is Boolean -> "boolean"
is Int -> "int"
is Number -> "float"
is String -> "string" is String -> "string"
is Number -> "number"
else -> throw IllegalArgumentException("Unknown Sensor State Type") else -> throw IllegalArgumentException("Unknown Sensor State Type")
} }
sensor.type = basicSensor.type sensor.type = basicSensor.type

View file

@ -32,6 +32,7 @@ class SensorReceiver : BroadcastReceiver() {
NetworkSensorManager(), NetworkSensorManager(),
NextAlarmManager(), NextAlarmManager(),
PhoneStateSensorManager(), PhoneStateSensorManager(),
PowerSensorManager(),
PressureSensorManager(), PressureSensorManager(),
ProximitySensorManager(), ProximitySensorManager(),
StepsSensorManager(), StepsSensorManager(),

View file

@ -154,6 +154,13 @@ like to connect to:</string>
<string name="sensor_description_location_zone">Import existing Home Assistant zones as geofences for zone based tracking.</string> <string name="sensor_description_location_zone">Import existing Home Assistant zones as geofences for zone based tracking.</string>
<string name="sensor_description_next_alarm">The date and time of the next scheduled alarm, any app or manufacturer can override the default behavior. The package attribute will tell you which app set the next scheduled alarm.</string> <string name="sensor_description_next_alarm">The date and time of the next scheduled alarm, any app or manufacturer can override the default behavior. The package attribute will tell you which app set the next scheduled alarm.</string>
<string name="sensor_description_none">No description</string> <string name="sensor_description_none">No description</string>
<string name="sensor_description_interactive">Whether or not the device is in an interactive state</string>
<string name="sensor_description_doze">Whether or not the device is in Doze mode</string>
<string name="sensor_description_power_save">Whether or not the device is in Power Save mode</string>
<string name="sensor_name_power">Power Sensors</string>
<string name="basic_sensor_name_power_save">Power Save</string>
<string name="basic_sensor_name_interactive">Interactive</string>
<string name="basic_sensor_name_doze">Doze Mode</string>
<string name="basic_sensor_name_activity">Detected Activity</string> <string name="basic_sensor_name_activity">Detected Activity</string>
<string name="basic_sensor_name_geolocation">Geocoded Location</string> <string name="basic_sensor_name_geolocation">Geocoded Location</string>
<string name="basic_sensor_name_location_background">Background Location</string> <string name="basic_sensor_name_location_background">Background Location</string>