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 ability for ClimateControl Card to cycle through entity states #4142

Merged
merged 8 commits into from
Feb 22, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE
import java.net.URL
import java.util.concurrent.TimeUnit
Expand All @@ -32,11 +31,10 @@ object CameraControl : HaControl {
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
val image = if (baseUrl != null && (entity.attributes["entity_picture"] as? String)?.isNotBlank() == true) {
getThumbnail(baseUrl + entity.attributes["entity_picture"] as String)
val image = if (info.baseUrl != null && (entity.attributes["entity_picture"] as? String)?.isNotBlank() == true) {
getThumbnail(info.baseUrl + entity.attributes["entity_picture"] as String)
} else {
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ import android.content.Context
import android.os.Build
import android.service.controls.Control
import android.service.controls.DeviceTypes
import android.service.controls.actions.BooleanAction
import android.service.controls.actions.ControlAction
import android.service.controls.actions.FloatAction
import android.service.controls.actions.ModeAction
import android.service.controls.templates.RangeTemplate
import android.service.controls.templates.TemperatureControlTemplate
import android.service.controls.templates.ToggleRangeTemplate
import androidx.annotation.RequiresApi
import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse

@RequiresApi(Build.VERSION_CODES.R)
object ClimateControl : HaControl {
private data class ClimateState(val currentMode: String, val supportedModes: ArrayList<String>)

private const val SUPPORT_TARGET_TEMPERATURE = 1
private const val SUPPORT_TARGET_TEMPERATURE_RANGE = 2
private val temperatureControlModes = mapOf(
Expand All @@ -31,13 +34,13 @@ object ClimateControl : HaControl {
"heat_cool" to TemperatureControlTemplate.FLAG_MODE_HEAT_COOL,
"off" to TemperatureControlTemplate.FLAG_MODE_OFF
)
private val climateStates = HashMap<String, ClimateState>()

override fun provideControlFeatures(
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
val minValue = (entity.attributes["min_temp"] as? Number)?.toFloat() ?: 0f
val maxValue = (entity.attributes["max_temp"] as? Number)?.toFloat() ?: 100f
Expand All @@ -60,22 +63,32 @@ object ClimateControl : HaControl {
}
val temperatureFormatSize = if (temperatureStepSize < 1f) "1" else "0"
val rangeTemplate = RangeTemplate(
entity.entityId,
info.systemId,
minValue,
maxValue,
currentValue,
temperatureStepSize,
"%.${temperatureFormatSize}f $temperatureUnit"
)
if (entityShouldBePresentedAsThermostat(entity)) {
val state = ClimateState(entity.state, ArrayList())
val toggleRangeTemplate = ToggleRangeTemplate(
info.systemId + "_range",
// Set checked to true to always show the temperature indicator, regardless of climate mode
true,
jpelgrom marked this conversation as resolved.
Show resolved Hide resolved
context.getString(commonR.string.widget_tap_action_toggle),
rangeTemplate
)
var modesFlag = 0
(entity.attributes["hvac_modes"] as? List<String>)?.forEach {
modesFlag = modesFlag or temperatureControlModeFlags[it]!!
state.supportedModes.add(it)
}
this.climateStates[info.systemId] = state
control.setControlTemplate(
TemperatureControlTemplate(
entity.entityId,
rangeTemplate,
info.systemId,
toggleRangeTemplate,
temperatureControlModes[entity.state]!!,
temperatureControlModes[entity.state]!!,
modesFlag
Expand All @@ -102,13 +115,18 @@ object ClimateControl : HaControl {
integrationRepository: IntegrationRepository,
action: ControlAction
): Boolean {
val entityStr: String = if (action.templateId.split(".").size > 2) {
action.templateId.split(".", limit = 2)[1]
} else {
action.templateId
}
return when (action) {
is FloatAction -> {
integrationRepository.callService(
action.templateId.split(".")[0],
entityStr.split(".")[0],
"set_temperature",
hashMapOf(
"entity_id" to action.templateId,
"entity_id" to entityStr,
"temperature" to (action as? FloatAction)?.newValue.toString()
)
)
Expand All @@ -119,7 +137,7 @@ object ClimateControl : HaControl {
action.templateId.split(".")[0],
"set_hvac_mode",
hashMapOf(
"entity_id" to action.templateId,
"entity_id" to entityStr,
"hvac_mode" to (
temperatureControlModes.entries.find {
it.value == ((action as? ModeAction)?.newMode ?: -1)
Expand All @@ -129,6 +147,23 @@ object ClimateControl : HaControl {
)
true
}
is BooleanAction -> {
if (this.climateStates[action.templateId] == null) {
return false
}
val supportedModes = this.climateStates[action.templateId]!!.supportedModes
val currentMode = this.climateStates[action.templateId]!!.currentMode
val nextMode = (supportedModes.indexOf(currentMode) + 1) % supportedModes.count()
integrationRepository.callService(
entityStr.split(".")[0],
"set_hvac_mode",
hashMapOf(
"entity_id" to entityStr,
"hvac_mode" to supportedModes[nextMode]
)
)
true
}
else -> {
false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.integration.getCoverPosition
import io.homeassistant.companion.android.common.data.integration.isActive
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse

@RequiresApi(Build.VERSION_CODES.R)
object CoverControl : HaControl {
Expand All @@ -26,8 +25,7 @@ object CoverControl : HaControl {
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
val position = entity.getCoverPosition()
control.setControlTemplate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.integration.domain
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
import io.homeassistant.companion.android.common.util.capitalize
import java.util.Locale

Expand All @@ -21,8 +20,7 @@ object DefaultButtonControl : HaControl {
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
control.setStatusText("")
control.setControlTemplate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import androidx.annotation.RequiresApi
import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
import kotlinx.coroutines.runBlocking

@RequiresApi(Build.VERSION_CODES.R)
Expand All @@ -20,8 +19,7 @@ object DefaultSliderControl : HaControl {
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
control.setStatusText("")
control.setControlTemplate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.integration.domain
import io.homeassistant.companion.android.common.data.integration.isActive
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
import io.homeassistant.companion.android.common.util.capitalize
import java.util.Locale

Expand All @@ -24,8 +23,7 @@ object DefaultSwitchControl : HaControl {
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
control.setControlTemplate(
ToggleTemplate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ import io.homeassistant.companion.android.common.data.integration.IntegrationRep
import io.homeassistant.companion.android.common.data.integration.getFanSpeed
import io.homeassistant.companion.android.common.data.integration.isActive
import io.homeassistant.companion.android.common.data.integration.supportsFanSetSpeed
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse

@RequiresApi(Build.VERSION_CODES.R)
object FanControl : HaControl {
override fun provideControlFeatures(
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
if (entity.supportsFanSetSpeed()) {
val position = entity.getFanSpeed()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.integration.domain
import io.homeassistant.companion.android.common.data.integration.friendlyState
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
import io.homeassistant.companion.android.webview.WebViewActivity

@RequiresApi(Build.VERSION_CODES.R)
Expand Down Expand Up @@ -77,15 +76,14 @@ interface HaControl {
}
}

return provideControlFeatures(context, control, entity, info.area, info.baseUrl).build()
return provideControlFeatures(context, control, entity, info).build()
}

fun provideControlFeatures(
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder

fun getDeviceType(entity: Entity<Map<String, Any>>): Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.annotation.RequiresApi
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.integration.domain
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
import io.homeassistant.companion.android.common.util.capitalize
import java.util.Locale

Expand All @@ -20,8 +19,7 @@ object HaFailedControl : HaControl {
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
control.setStatus(if (entity.state == "notfound") Control.STATUS_NOT_FOUND else Control.STATUS_ERROR)
control.setStatusText("")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ import io.homeassistant.companion.android.common.data.integration.IntegrationRep
import io.homeassistant.companion.android.common.data.integration.getLightBrightness
import io.homeassistant.companion.android.common.data.integration.isActive
import io.homeassistant.companion.android.common.data.integration.supportsLightBrightness
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse

@RequiresApi(Build.VERSION_CODES.R)
object LightControl : HaControl {
override fun provideControlFeatures(
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
val position = entity.getLightBrightness()
control.setControlTemplate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.integration.isActive
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse

@RequiresApi(Build.VERSION_CODES.R)
object LockControl : HaControl {
override fun provideControlFeatures(
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
control.setControlTemplate(
ToggleTemplate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
import io.homeassistant.companion.android.common.data.integration.isActive
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse

@RequiresApi(Build.VERSION_CODES.R)
object VacuumControl : HaControl {
Expand All @@ -24,8 +23,7 @@ object VacuumControl : HaControl {
context: Context,
control: Control.StatefulBuilder,
entity: Entity<Map<String, Any>>,
area: AreaRegistryResponse?,
baseUrl: String?
info: HaControlInfo
): Control.StatefulBuilder {
entitySupportedFeatures = entity.attributes["supported_features"] as Int
control.setControlTemplate(
Expand Down