diff --git a/WearOS/src/src/debug/java/com/chriskaczor/homemonitor/wear/TilePreviewActivity.kt b/WearOS/src/src/debug/java/com/chriskaczor/homemonitor/wear/TilePreviewActivity.kt index 312a86a..6c0c60c 100644 --- a/WearOS/src/src/debug/java/com/chriskaczor/homemonitor/wear/TilePreviewActivity.kt +++ b/WearOS/src/src/debug/java/com/chriskaczor/homemonitor/wear/TilePreviewActivity.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.widget.FrameLayout import androidx.activity.ComponentActivity import androidx.wear.tiles.manager.TileUiClient +import com.chriskaczor.homemonitor.wear.power.PowerTileService class TilePreviewActivity : ComponentActivity() { lateinit var tileUiClient: TileUiClient diff --git a/WearOS/src/src/main/AndroidManifest.xml b/WearOS/src/src/main/AndroidManifest.xml index 0610587..cd1885e 100644 --- a/WearOS/src/src/main/AndroidManifest.xml +++ b/WearOS/src/src/main/AndroidManifest.xml @@ -18,9 +18,9 @@ android:value="true" /> @@ -29,7 +29,22 @@ + android:resource="@drawable/tile_power" /> + + + + + + + + diff --git a/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/PowerRepository.kt b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/power/PowerRepository.kt similarity index 51% rename from WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/PowerRepository.kt rename to WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/power/PowerRepository.kt index 59ad660..e1b5e3a 100644 --- a/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/PowerRepository.kt +++ b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/power/PowerRepository.kt @@ -1,4 +1,4 @@ -package com.chriskaczor.homemonitor.wear +package com.chriskaczor.homemonitor.wear.power import com.beust.klaxon.Json import com.beust.klaxon.Klaxon @@ -17,16 +17,11 @@ data class PowerStatus( ) object PowerRepository { - suspend fun getPowerStatus(): PowerStatus { + suspend fun getPowerStatus(): PowerStatus? { val json = URL("http://home.kaczorzoo.net/api/power/status/recent").readText(); - val data = Klaxon().parse(json) ?: return powerStatus; - - powerStatus = powerStatus.copy(generation = data.generation, consumption = data.consumption, timestamp = data.timestamp); + val powerStatus = Klaxon().parse(json); return powerStatus; } } - -var powerStatus = - PowerStatus(generation = 0, consumption = 0, timestamp = Timestamp(System.currentTimeMillis()).toString()) diff --git a/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/PowerTileService.kt b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/power/PowerTileService.kt similarity index 93% rename from WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/PowerTileService.kt rename to WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/power/PowerTileService.kt index 3491513..bbefe6a 100644 --- a/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/PowerTileService.kt +++ b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/power/PowerTileService.kt @@ -1,4 +1,4 @@ -package com.chriskaczor.homemonitor.wear +package com.chriskaczor.homemonitor.wear.power import androidx.core.content.ContextCompat import androidx.wear.tiles.ActionBuilders @@ -15,6 +15,7 @@ import androidx.wear.tiles.TileBuilders.Tile import androidx.wear.tiles.TileService import androidx.wear.tiles.TimelineBuilders.Timeline import androidx.wear.tiles.TimelineBuilders.TimelineEntry +import com.chriskaczor.homemonitor.wear.R import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel @@ -95,17 +96,17 @@ class PowerTileService : TileService() { serviceScope.cancel() } - private fun layout(goalProgress: PowerStatus, deviceParameters: DeviceParameters) = + private fun layout(goalProgress: PowerStatus?, deviceParameters: DeviceParameters) = Box.Builder() .setWidth(expand()) .setHeight(expand()) .addContent( Column.Builder() .addContent( - generationLayout(goalProgress.generation, deviceParameters) + generationLayout(goalProgress?.generation ?: -1, deviceParameters) ) .addContent( - consumptionLayout(goalProgress.consumption, deviceParameters) + consumptionLayout(goalProgress?.consumption ?: -1, deviceParameters) ) .addContent(Spacer.Builder().setHeight(VERTICAL_SPACING_HEIGHT).build()) .addContent(refreshButton()) @@ -135,7 +136,7 @@ class PowerTileService : TileService() { ) .addContent( Text.Builder() - .setText(generation.toString()) + .setText(if (generation <= 0) "0" else generation.toString()) .setFontStyle(FontStyles.display3(deviceParameters).build()) .build() ).build() @@ -163,7 +164,7 @@ class PowerTileService : TileService() { ) .addContent( Text.Builder() - .setText(consumption.toString()) + .setText(if (consumption <= 0) "0" else consumption.toString()) .setFontStyle(FontStyles.display3(deviceParameters).build()) .build() ).build() diff --git a/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/weather/WeatherRepository.kt b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/weather/WeatherRepository.kt new file mode 100644 index 0000000..14467f3 --- /dev/null +++ b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/weather/WeatherRepository.kt @@ -0,0 +1,24 @@ +package com.chriskaczor.homemonitor.wear.weather + +import com.beust.klaxon.Json +import com.beust.klaxon.Klaxon +import java.net.URL +import java.sql.Timestamp + +data class WeatherStatus( + @Json(name = "humidity") + val humidity: Double, + + @Json(name = "pressure") + val pressure: Double, +) + +object WeatherRepository { + suspend fun getWeatherStatus(): WeatherStatus? { + var json = URL("http://home.kaczorzoo.net/api/weather/readings/recent").readText(); + + var weatherStatus = Klaxon().parse(json); + + return weatherStatus; + } +} diff --git a/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/weather/WeatherTileService.kt b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/weather/WeatherTileService.kt new file mode 100644 index 0000000..f8fdd6c --- /dev/null +++ b/WearOS/src/src/main/java/com/chriskaczor/homemonitor/wear/weather/WeatherTileService.kt @@ -0,0 +1,200 @@ +package com.chriskaczor.homemonitor.wear.weather + +import androidx.core.content.ContextCompat +import androidx.wear.tiles.ActionBuilders +import androidx.wear.tiles.ColorBuilders.argb +import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters +import androidx.wear.tiles.DimensionBuilders.dp +import androidx.wear.tiles.DimensionBuilders.expand +import androidx.wear.tiles.LayoutElementBuilders.* +import androidx.wear.tiles.ModifiersBuilders.* +import androidx.wear.tiles.RequestBuilders.ResourcesRequest +import androidx.wear.tiles.RequestBuilders.TileRequest +import androidx.wear.tiles.ResourceBuilders.* +import androidx.wear.tiles.TileBuilders.Tile +import androidx.wear.tiles.TileService +import androidx.wear.tiles.TimelineBuilders.Timeline +import androidx.wear.tiles.TimelineBuilders.TimelineEntry +import com.chriskaczor.homemonitor.wear.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.guava.future + +private const val RESOURCES_VERSION = "1" + +// dimensions +private val BUTTON_SIZE = dp(48f) +private val BUTTON_RADIUS = dp(24f) +private val BUTTON_PADDING = dp(12f) +private val VERTICAL_SPACING_HEIGHT = dp(8f) + +// identifiers +private const val ID_IMAGE_REFRESH = "image_refresh" +private const val ID_IMAGE_GENERATION = "image_generation" +private const val ID_IMAGE_CONSUMPTION = "image_consumption" +private const val ID_CLICK_REFRESH = "click_refresh" + +class WeatherTileService : TileService() { + private val serviceScope = CoroutineScope(Dispatchers.IO) + + override fun onTileRequest(requestParams: TileRequest) = serviceScope.future { + val powerStatus = WeatherRepository.getWeatherStatus() + + val deviceParams = requestParams.deviceParameters!! + + Tile.Builder() + .setResourcesVersion(RESOURCES_VERSION) + .setTimeline( + Timeline.Builder() + .addTimelineEntry( + TimelineEntry.Builder() + .setLayout( + Layout.Builder() + .setRoot( + layout(powerStatus, deviceParams) + ).build() + ).build() + ).build() + ).build() + } + + override fun onResourcesRequest(requestParams: ResourcesRequest) = serviceScope.future { + Resources.Builder() + .setVersion(RESOURCES_VERSION) + .addIdToImageMapping( + ID_IMAGE_REFRESH, + ImageResource.Builder() + .setAndroidResourceByResId( + AndroidImageResourceByResId.Builder() + .setResourceId(R.drawable.ic_refresh) + .build() + ).build() + ) + .addIdToImageMapping( + ID_IMAGE_GENERATION, + ImageResource.Builder() + .setAndroidResourceByResId( + AndroidImageResourceByResId.Builder() + .setResourceId(R.drawable.ic_sun) + .build() + ).build() + ) + .addIdToImageMapping( + ID_IMAGE_CONSUMPTION, + ImageResource.Builder() + .setAndroidResourceByResId( + AndroidImageResourceByResId.Builder() + .setResourceId(R.drawable.ic_plug) + .build() + ).build() + ).build() + } + + override fun onDestroy() { + super.onDestroy() + serviceScope.cancel() + } + + private fun layout(goalProgress: WeatherStatus?, deviceParameters: DeviceParameters) = + Box.Builder() + .setWidth(expand()) + .setHeight(expand()) + .addContent( + Column.Builder() + .addContent( + generationLayout(goalProgress?.humidity ?: -1.0, deviceParameters) + ) + .addContent( + consumptionLayout(goalProgress?.pressure ?: -1.0, deviceParameters) + ) + .addContent(Spacer.Builder().setHeight(VERTICAL_SPACING_HEIGHT).build()) + .addContent(refreshButton()) + .build() + ).build() + + private fun generationLayout(generation: Double, deviceParameters: DeviceParameters) = + Row.Builder() + .addContent( + Image.Builder() + .setHeight(dp(36f)) + .setWidth(dp(36f)) + .setModifiers( + Modifiers.Builder() + .setPadding( + Padding.Builder() + .setStart(dp(0f)) + .setEnd(dp(10f)) + .setTop(dp(1f)) + .setBottom(dp(0f)) + .build() + ) + .build() + ) + .setResourceId(ID_IMAGE_GENERATION) + .build() + ) + .addContent( + Text.Builder() + .setText(if (generation <= 0) "0" else generation.toString()) + .setFontStyle(FontStyles.display3(deviceParameters).build()) + .build() + ).build() + + private fun consumptionLayout(consumption: Double, deviceParameters: DeviceParameters) = + Row.Builder() + .addContent( + Image.Builder() + .setHeight(dp(36f)) + .setWidth(dp(36f)) + .setModifiers( + Modifiers.Builder() + .setPadding( + Padding.Builder() + .setStart(dp(0f)) + .setEnd(dp(10f)) + .setTop(dp(1f)) + .setBottom(dp(0f)) + .build() + ) + .build() + ) + .setResourceId(ID_IMAGE_CONSUMPTION) + .build() + ) + .addContent( + Text.Builder() + .setText(if (consumption <= 0) "0" else consumption.toString()) + .setFontStyle(FontStyles.display3(deviceParameters).build()) + .build() + ).build() + + private fun refreshButton() = + Image.Builder() + .setWidth(BUTTON_SIZE) + .setHeight(BUTTON_SIZE) + .setResourceId(ID_IMAGE_REFRESH) + .setModifiers( + Modifiers.Builder() + .setPadding( + Padding.Builder() + .setStart(BUTTON_PADDING) + .setEnd(BUTTON_PADDING) + .setTop(BUTTON_PADDING) + .setBottom(BUTTON_PADDING) + .build() + ) + .setBackground( + Background.Builder() + .setCorner(Corner.Builder().setRadius(BUTTON_RADIUS).build()) + .setColor(argb(ContextCompat.getColor(this, R.color.primaryDark))) + .build() + ) + .setClickable( + Clickable.Builder() + .setId(ID_CLICK_REFRESH) + .setOnClick(ActionBuilders.LoadAction.Builder().build()) + .build() + ).build() + ).build() +} diff --git a/WearOS/src/src/main/res/drawable/tile_goals.png b/WearOS/src/src/main/res/drawable/tile_goals.png deleted file mode 100644 index 6e20270..0000000 Binary files a/WearOS/src/src/main/res/drawable/tile_goals.png and /dev/null differ diff --git a/WearOS/src/src/main/res/drawable/tile_power.png b/WearOS/src/src/main/res/drawable/tile_power.png new file mode 100644 index 0000000..17fdc02 Binary files /dev/null and b/WearOS/src/src/main/res/drawable/tile_power.png differ diff --git a/WearOS/src/src/main/res/drawable/tile_weather.png b/WearOS/src/src/main/res/drawable/tile_weather.png new file mode 100644 index 0000000..2962bad Binary files /dev/null and b/WearOS/src/src/main/res/drawable/tile_weather.png differ diff --git a/WearOS/src/src/main/res/values/strings.xml b/WearOS/src/src/main/res/values/strings.xml index af3adc2..9c5f799 100644 --- a/WearOS/src/src/main/res/values/strings.xml +++ b/WearOS/src/src/main/res/values/strings.xml @@ -4,4 +4,5 @@ Home Monitor Power + Weather