Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sensors to report car energy profile #3688

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.util.Log
import androidx.annotation.RequiresApi
import androidx.car.app.hardware.common.CarValue
import androidx.car.app.hardware.info.EnergyLevel
import androidx.car.app.hardware.info.EnergyProfile
import androidx.car.app.hardware.info.EvStatus
import androidx.car.app.hardware.info.Mileage
import androidx.car.app.hardware.info.Model
Expand Down Expand Up @@ -72,29 +73,51 @@ class CarSensorManager :
deviceClass = "distance"
)

private val fuelType = SensorManager.BasicSensor(
"car_fuel_type",
"sensor",
R.string.basic_sensor_name_car_fuel_type,
R.string.sensor_description_car_fuel_type,
"mdi:gas-station",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC
)

private val evConnector = SensorManager.BasicSensor(
"car_ev_connector",
"sensor",
R.string.basic_sensor_name_car_ev_connector_type,
R.string.sensor_description_car_ev_connector_type,
"mdi:car-electric",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC
)

private val sensorsList = listOf(
batteryLevel,
carName,
carStatus,
evConnector,
fuelLevel,
fuelType,
odometerValue
)

private enum class Listener {
ENERGY, MODEL, MILEAGE, STATUS,
ENERGY, MODEL, MILEAGE, STATUS, PROFILE
}

private val listenerSensors = mapOf(
Listener.ENERGY to listOf(batteryLevel, fuelLevel),
Listener.MODEL to listOf(carName),
Listener.STATUS to listOf(carStatus),
Listener.MILEAGE to listOf(odometerValue)
Listener.MILEAGE to listOf(odometerValue),
Listener.PROFILE to listOf(evConnector, fuelType)
)
private val listenerLastRegistered = mutableMapOf(
Listener.ENERGY to -1L,
Listener.MODEL to -1L,
Listener.STATUS to -1L,
Listener.MILEAGE to -1L
Listener.MILEAGE to -1L,
Listener.PROFILE to -1L
)
}

Expand All @@ -119,7 +142,7 @@ class CarSensorManager :

override fun requiredPermissions(sensorId: String): Array<String> {
return when {
(sensorId == fuelLevel.id || sensorId == batteryLevel.id) -> {
(sensorId == fuelLevel.id || sensorId == batteryLevel.id || sensorId == fuelType.id || sensorId == evConnector.id) -> {
arrayOf("com.google.android.gms.permission.CAR_FUEL")
}
sensorId == odometerValue.id -> {
Expand Down Expand Up @@ -162,40 +185,47 @@ class CarSensorManager :

@androidx.annotation.OptIn(androidx.car.app.annotations.ExperimentalCarApi::class)
private fun setListener(l: Listener, enable: Boolean) {
val car = HaCarAppService.carInfo ?: return

if (enable) {
Log.d(TAG, "registering CarInfo $l listener")
} else {
Log.d(TAG, "unregistering CarInfo $l listener")
}

val car = HaCarAppService.carInfo ?: return
val executor = ContextCompat.getMainExecutor(context)
when (l) {
Listener.ENERGY -> {
if (enable) {
car.addEnergyLevelListener(ContextCompat.getMainExecutor(context), ::onEnergyAvailable)
car.addEnergyLevelListener(executor, ::onEnergyAvailable)
} else {
car.removeEnergyLevelListener(::onEnergyAvailable)
}
}
Listener.MILEAGE -> {
if (enable) {
car.addMileageListener(ContextCompat.getMainExecutor(context), ::onMileageAvailable)
car.addMileageListener(executor, ::onMileageAvailable)
} else {
car.removeMileageListener(::onMileageAvailable)
}
}
Listener.MODEL -> {
if (enable) {
car.fetchModel(ContextCompat.getMainExecutor(context), ::onModelAvailable)
car.fetchModel(executor, ::onModelAvailable)
}
}
Listener.STATUS -> {
if (enable) {
car.addEvStatusListener(ContextCompat.getMainExecutor(context), ::onStatusAvailable)
car.addEvStatusListener(executor, ::onStatusAvailable)
} else {
car.removeEvStatusListener(::onStatusAvailable)
}
}
Listener.PROFILE -> {
if (enable) {
car.fetchEnergyProfile(executor, ::onProfileAvailable)
}
}
}

if (enable) {
Expand Down Expand Up @@ -297,6 +327,30 @@ class CarSensorManager :
setListener(Listener.MILEAGE, false)
}

private fun onProfileAvailable(data: EnergyProfile) {
val fuelTypeStatus = carValueStatus(data.fuelTypes.status)
val evConnectorTypeStatus = carValueStatus(data.evConnectorTypes.status)
Log.d(TAG, "Received energy profile: $data")
if (isEnabled(context, fuelType)) {
onSensorUpdated(
context,
fuelType,
fuelTypeStatus ?: getFuelType(data.fuelTypes.value!!),
fuelType.statelessIcon,
mapOf()
)
}
if (isEnabled(context, evConnector)) {
onSensorUpdated(
context,
evConnector,
evConnectorTypeStatus ?: getEvConnectorType(data.evConnectorTypes.value!!),
evConnector.statelessIcon,
mapOf()
)
}
}

private fun carValueStatus(value: Int): String? {
return when (value) {
CarValue.STATUS_SUCCESS -> null
Expand All @@ -306,4 +360,50 @@ class CarSensorManager :
else -> "unavailable"
}
}

private fun getFuelType(values: List<Int>): String {
val fuelTypeList = emptyList<String>().toMutableList()
values.forEach {
fuelTypeList += when (it) {
EnergyProfile.FUEL_TYPE_BIODIESEL -> "Biodiesel"
EnergyProfile.FUEL_TYPE_CNG -> "Compressed natural gas"
EnergyProfile.FUEL_TYPE_DIESEL_1 -> "#1 Grade Diesel"
EnergyProfile.FUEL_TYPE_DIESEL_2 -> "#2 Grade Diesel"
EnergyProfile.FUEL_TYPE_E85 -> "85% ethanol/gasoline blend"
EnergyProfile.FUEL_TYPE_ELECTRIC -> "Electric"
EnergyProfile.FUEL_TYPE_HYDROGEN -> "Hydrogen fuel cell"
EnergyProfile.FUEL_TYPE_LEADED -> "Leaded gasoline"
EnergyProfile.FUEL_TYPE_LNG -> "Liquified natural gas"
EnergyProfile.FUEL_TYPE_LPG -> "Liquified petroleum gas"
EnergyProfile.FUEL_TYPE_OTHER -> "Other"
EnergyProfile.FUEL_TYPE_UNKNOWN -> "unknown"
EnergyProfile.FUEL_TYPE_UNLEADED -> "Unleaded gasoline"
else -> "unknown"
}
}
return fuelTypeList.toString()
}

private fun getEvConnectorType(values: List<Int>): String {
val evConnectorList = emptyList<String>().toMutableList()
values.forEach {
evConnectorList += when (it) {
EnergyProfile.EVCONNECTOR_TYPE_CHADEMO -> "CHAdeMo fast charger connector"
EnergyProfile.EVCONNECTOR_TYPE_COMBO_1 -> "Combined Charging System Combo 1"
EnergyProfile.EVCONNECTOR_TYPE_COMBO_2 -> "Combined Charging System Combo 2"
EnergyProfile.EVCONNECTOR_TYPE_GBT -> "GBT_AC Fast Charging Standard"
EnergyProfile.EVCONNECTOR_TYPE_GBT_DC -> "GBT_DC Fast Charging Standard"
EnergyProfile.EVCONNECTOR_TYPE_J1772 -> "Connector type SAE J1772"
EnergyProfile.EVCONNECTOR_TYPE_MENNEKES -> "IEC 62196 Type 2 connector"
EnergyProfile.EVCONNECTOR_TYPE_OTHER -> "other"
EnergyProfile.EVCONNECTOR_TYPE_SCAME -> "IEC_TYPE_3_AC connector"
EnergyProfile.EVCONNECTOR_TYPE_TESLA_HPWC -> "High Power Wall Charger of Tesla"
EnergyProfile.EVCONNECTOR_TYPE_TESLA_ROADSTER -> "Connector of Tesla Roadster"
EnergyProfile.EVCONNECTOR_TYPE_TESLA_SUPERCHARGER -> "Supercharger of Tesla"
EnergyProfile.EVCONNECTOR_TYPE_UNKNOWN -> "unknown"
else -> "unknown"
}
}
return evConnectorList.toString()
}
}
4 changes: 4 additions & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1117,4 +1117,8 @@
<string name="only_favorites">Only Show Favorites</string>
<string name="beacon_scanning">Beacon Monitor Scanning</string>
<string name="car_data_unavailable">Open Home Assistant app to activate the sensor</string>
<string name="basic_sensor_name_car_fuel_type">Car Fuel Type</string>
<string name="sensor_description_car_fuel_type">List of available fuel types for the connected car</string>
<string name="basic_sensor_name_car_ev_connector_type">Car EV Connector Type</string>
<string name="sensor_description_car_ev_connector_type">List of available EV connectors for the connected car</string>
</resources>