Skip to content
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 @@ -28,6 +28,7 @@ class LaunchPresenterImpl @Inject constructor(
getMessagingToken()
)
)
serverManager.integrationRepository(it.id).getConfig() // Update cached data
} catch (e: Exception) {
Log.e(TAG, "Issue updating Registration", e)
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ServerSettingsPresenterImpl @Inject constructor(

override fun getBoolean(key: String?, defValue: Boolean): Boolean = runBlocking {
when (key) {
"trust_server" -> serverManager.integrationRepository(serverId).isTrusted()
"app_lock" -> serverManager.authenticationRepository(serverId).isLockEnabledRaw()
"app_lock_home_bypass" -> serverManager.authenticationRepository(serverId).isLockHomeBypassEnabled()
else -> throw IllegalArgumentException("No boolean found by this key: $key")
Expand All @@ -41,6 +42,7 @@ class ServerSettingsPresenterImpl @Inject constructor(
override fun putBoolean(key: String?, value: Boolean) {
mainScope.launch {
when (key) {
"trust_server" -> serverManager.integrationRepository(serverId).setTrusted(value)
"app_lock" -> serverManager.authenticationRepository(serverId).setLockEnabled(value)
"app_lock_home_bypass" -> serverManager.authenticationRepository(serverId).setLockHomeBypassEnabled(value)
else -> throw IllegalArgumentException("No boolean found by this key: $key")
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_settings_remote.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/colorAccent" android:pathData="M15,9L9,9c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1L16,10c0,-0.55 -0.45,-1 -1,-1zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM7.05,6.05l1.41,1.41C9.37,6.56 10.62,6 12,6s2.63,0.56 3.54,1.46l1.41,-1.41C15.68,4.78 13.93,4 12,4s-3.68,0.78 -4.95,2.05zM12,0C8.96,0 6.21,1.23 4.22,3.22l1.41,1.41C7.26,3.01 9.51,2 12,2s4.74,1.01 6.36,2.64l1.41,-1.41C17.79,1.23 15.04,0 12,0z"/>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/xml/preferences_server.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
<PreferenceCategory
android:key="security_category"
android:title="@string/security">
<SwitchPreference
android:key="trust_server"
android:defaultValue="true"
android:icon="@drawable/ic_settings_remote"
android:title="@string/trust_server_title"
android:summary="@string/trust_server_summary"/>
<SwitchPreference
android:key="app_lock"
android:icon="@drawable/ic_lock"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class LaunchPresenterImpl @Inject constructor(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
)
)
serverManager.integrationRepository(it.id).getConfig() // Update cached data
} catch (e: Exception) {
Log.e(TAG, "Issue updating Registration", e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ interface IntegrationRepository {
suspend fun registerSensor(sensorRegistration: SensorRegistration<Any>)
suspend fun updateSensors(sensors: Array<SensorRegistration<Any>>): Boolean

suspend fun isTrusted(): Boolean

suspend fun setTrusted(trusted: Boolean)

suspend fun shouldNotifySecurityWarning(): Boolean

suspend fun getConversation(speech: String): String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class IntegrationRepositoryImpl @AssistedInject constructor(
private const val PREF_CHECK_SENSOR_REGISTRATION_NEXT = "sensor_reg_last"
private const val PREF_SESSION_TIMEOUT = "session_timeout"
private const val PREF_SESSION_EXPIRE = "session_expire"
private const val PREF_TRUSTED = "trusted"
private const val PREF_SEC_WARNING_NEXT = "sec_warning_last"
private const val TAG = "IntegrationRepository"
private const val RATE_LIMIT_URL = BuildConfig.RATE_LIMIT_URL
Expand Down Expand Up @@ -157,6 +158,7 @@ class IntegrationRepositoryImpl @AssistedInject constructor(
localStorage.remove("${serverId}_$PREF_CHECK_SENSOR_REGISTRATION_NEXT")
localStorage.remove("${serverId}_$PREF_SESSION_TIMEOUT")
localStorage.remove("${serverId}_$PREF_SESSION_EXPIRE")
localStorage.remove("${serverId}_$PREF_TRUSTED")
localStorage.remove("${serverId}_$PREF_SEC_WARNING_NEXT")
// app version and push token is device-specific
}
Expand Down Expand Up @@ -393,6 +395,12 @@ class IntegrationRepositoryImpl @AssistedInject constructor(
private suspend fun getSessionExpireMillis(): Long =
localStorage.getLong("${serverId}_$PREF_SESSION_EXPIRE") ?: 0

override suspend fun isTrusted(): Boolean =
localStorage.getBooleanOrNull("${serverId}_$PREF_TRUSTED") ?: true

override suspend fun setTrusted(trusted: Boolean) =
localStorage.putBoolean("${serverId}_$PREF_TRUSTED", trusted)

override suspend fun getNotificationRateLimits(): RateLimitResponse {
val pushToken = localStorage.getString(PREF_PUSH_TOKEN) ?: ""
val requestBody = RateLimitRequest(pushToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,19 +207,22 @@ abstract class SensorReceiverBase : BroadcastReceiver() {
): Boolean {
val currentHAversion = serverManager.integrationRepository(server.id).getHomeAssistantVersion()
val supportsDisabledSensors = serverManager.integrationRepository(server.id).isHomeAssistantVersionAtLeast(2022, 6, 0)
val coreSensorStatus: Map<String, Boolean>? = if (supportsDisabledSensors) {
try {
val config = serverManager.integrationRepository(server.id).getConfig().entities
config
?.filter { it.value["disabled"] != null }
?.mapValues { !(it.value["disabled"] as Boolean) } // Map to sensor id -> enabled
} catch (e: Exception) {
Log.e(tag, "Error while getting core config to sync sensor status", e)
val serverIsTrusted = serverManager.integrationRepository(server.id).isTrusted()
val coreSensorStatus: Map<String, Boolean>? =
if (supportsDisabledSensors && (serverIsTrusted || (sensorDao.getEnabledCount() ?: 0) > 0)) {
try {
val config = serverManager.integrationRepository(server.id).getConfig().entities
config
?.filter { it.value["disabled"] != null }
?.mapValues { !(it.value["disabled"] as Boolean) } // Map to sensor id -> enabled
} catch (e: Exception) {
Log.e(tag, "Error while getting core config to sync sensor status", e)
null
}
} else {
// Cannot sync disabled, or all sensors disabled and server changes aren't trusted
null
}
} else {
null
}

var serverIsReachable = true
val enabledRegistrations = mutableListOf<SensorRegistration<Any>>()
Expand Down Expand Up @@ -268,9 +271,12 @@ abstract class SensorReceiverBase : BroadcastReceiver() {
sensorCoreEnabled != sensor.registered
) {
// 2. Try updating the sensor enabled state to match core state when it's different from
// the app, if the sensor can be registered and on core >= 2022.6
// the app, if the sensor can be registered, on core >= 2022.6 and server trusted.
// If the server isn't trusted, update registered state to match app.
try {
if (sensorCoreEnabled) { // App disabled, should enable
if (!serverIsTrusted) { // Core changed, but app doesn't trust server so 'override'
registerSensor(context, serverManager, fullSensor, basicSensor)
} else if (sensorCoreEnabled) { // App disabled, should enable
if (manager.checkPermission(context.applicationContext, basicSensor.id)) {
sensor.enabled = true
sensor.registered = true
Expand Down
2 changes: 2 additions & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,8 @@
<string name="tile_updated">Tile Data updated</string>
<string name="tiles">Tiles</string>
<string name="toast_message">%1$s was selected</string>
<string name="trust_server_title">Remotely control app &amp; device</string>
<string name="trust_server_summary">Manage enabled sensors and use notification commands from this server</string>
<string name="tts_error">Unable to process notification \"%1$s\" as text to speech.</string>
<string name="tts_no_text">Please set the text for text to speech to process</string>
<string name="unable_to_register">Unable to Register Application</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import io.homeassistant.companion.android.database.sensor.SensorDao
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.json.JSONObject
import javax.inject.Inject

Expand All @@ -49,25 +50,29 @@ class MessagingManager @Inject constructor(
val jsonObject = jsonData?.let { JSONObject(it) }
val serverId = jsonData?.get(NotificationData.WEBHOOK_ID)?.let {
serverManager.getServer(webhookId = it)?.id
}
} ?: ServerManager.SERVER_ID_ACTIVE
val notificationRow =
NotificationItem(0, now, notificationData[NotificationData.MESSAGE].toString(), jsonObject.toString(), source, serverId)
notificationDao.add(notificationRow)

when (notificationData[NotificationData.MESSAGE]) {
DeviceCommandData.COMMAND_BEACON_MONITOR -> {
if (!commandBeaconMonitor(context, notificationData)) {
sendNotification(notificationData, now)
mainScope.launch {
val allowCommands = serverManager.integrationRepository(serverId).isTrusted()
val message = notificationData[NotificationData.MESSAGE]
when {
message == DeviceCommandData.COMMAND_BEACON_MONITOR && allowCommands -> {
if (!commandBeaconMonitor(context, notificationData)) {
sendNotification(notificationData, now)
}
}
}
DeviceCommandData.COMMAND_BLE_TRANSMITTER -> {
if (!commandBleTransmitter(context, notificationData, sensorDao, mainScope)) {
sendNotification(notificationData)
message == DeviceCommandData.COMMAND_BLE_TRANSMITTER && allowCommands -> {
if (!commandBleTransmitter(context, notificationData, sensorDao, mainScope)) {
sendNotification(notificationData)
}
}
message == TextToSpeechData.TTS -> speakText(context, notificationData)
message == TextToSpeechData.COMMAND_STOP_TTS -> stopTTS()
else -> sendNotification(notificationData, now)
}
TextToSpeechData.TTS -> speakText(context, notificationData)
TextToSpeechData.COMMAND_STOP_TTS -> stopTTS()
else -> sendNotification(notificationData, now)
}
}

Expand Down