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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Change Log
2.3.0
-----

## Added:

- HomeKit module for integrating with HomeKit ecosystem.

### Fixed:

- Native targets now use Curl based Http client, which should support HTTPS
Expand Down
1 change: 1 addition & 0 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public final class inkapplications/shade/core/Shade {
public final fun getConfiguration ()Linkapplications/shade/structures/HueConfigurationContainer;
public final fun getDevices ()Linkapplications/shade/devices/DeviceControls;
public final fun getGroupedLights ()Linkapplications/shade/groupedlights/GroupedLightControls;
public final fun getHomekit ()Linkapplications/shade/homekit/HomekitControls;
public final fun getLights ()Linkapplications/shade/lights/LightControls;
public final fun getOnlineDiscovery ()Linkapplications/shade/discover/BridgeDiscovery;
public final fun getResources ()Linkapplications/shade/resources/ResourceControls;
Expand Down
1 change: 1 addition & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ kotlin {
api(projects.scenes)
api(projects.structures)
api(projects.zones)
api(projects.homekit)

api(libs.kimchi.logger)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import inkapplications.shade.devices.ShadeDevicesModule
import inkapplications.shade.discover.DiscoverModule
import inkapplications.shade.events.EventsModule
import inkapplications.shade.groupedlights.ShadeGroupedLightsModule
import inkapplications.shade.homekit.ShadeHomekitModule
import inkapplications.shade.internals.InternalsModule
import inkapplications.shade.lights.ShadeLightsModule
import inkapplications.shade.resources.ShadeResourcesModule
Expand Down Expand Up @@ -57,4 +58,5 @@ class Shade(
val groupedLights = ShadeGroupedLightsModule(internalsModule, eventsModule).groupedLights
val resources = ShadeResourcesModule(internalsModule).resources
val scenes = ShadeScenesModule(internalsModule).scenes
val homekit = ShadeHomekitModule(internalsModule).homekit
}
135 changes: 135 additions & 0 deletions homekit/api/homekit.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
public abstract interface class inkapplications/shade/homekit/HomekitControls {
public abstract fun getHomekit-klA6Vuc (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun listHomekit (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun updateHomekit-bKh5c1I (Ljava/lang/String;Linkapplications/shade/homekit/parameters/HomekitUpdateParameters;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class inkapplications/shade/homekit/ShadeHomekitModule {
public fun <init> (Linkapplications/shade/internals/InternalsModule;)V
public final fun getHomekit ()Linkapplications/shade/homekit/HomekitControls;
}

public final class inkapplications/shade/homekit/parameters/HomekitUpdateParameters {
public static final field Companion Linkapplications/shade/homekit/parameters/HomekitUpdateParameters$Companion;
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1-135DQNc ()Ljava/lang/String;
public final fun copy-R0FzwdQ (Ljava/lang/String;)Linkapplications/shade/homekit/parameters/HomekitUpdateParameters;
public static synthetic fun copy-R0FzwdQ$default (Linkapplications/shade/homekit/parameters/HomekitUpdateParameters;Ljava/lang/String;ILjava/lang/Object;)Linkapplications/shade/homekit/parameters/HomekitUpdateParameters;
public fun equals (Ljava/lang/Object;)Z
public final fun getAction-135DQNc ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final synthetic class inkapplications/shade/homekit/parameters/HomekitUpdateParameters$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Linkapplications/shade/homekit/parameters/HomekitUpdateParameters$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Linkapplications/shade/homekit/parameters/HomekitUpdateParameters;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Linkapplications/shade/homekit/parameters/HomekitUpdateParameters;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}

public final class inkapplications/shade/homekit/parameters/HomekitUpdateParameters$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class inkapplications/shade/homekit/structures/Homekit {
public static final field Companion Linkapplications/shade/homekit/structures/Homekit$Companion;
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1-XbiYvy0 ()Ljava/lang/String;
public final fun component2-1y3a6N0 ()Ljava/lang/String;
public final fun component3-iTYh7UM ()Ljava/lang/String;
public final fun copy-r9E5AgY (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Linkapplications/shade/homekit/structures/Homekit;
public static synthetic fun copy-r9E5AgY$default (Linkapplications/shade/homekit/structures/Homekit;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Linkapplications/shade/homekit/structures/Homekit;
public fun equals (Ljava/lang/Object;)Z
public final fun getId-XbiYvy0 ()Ljava/lang/String;
public final fun getStatus-iTYh7UM ()Ljava/lang/String;
public final fun getType-1y3a6N0 ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final synthetic class inkapplications/shade/homekit/structures/Homekit$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Linkapplications/shade/homekit/structures/Homekit$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Linkapplications/shade/homekit/structures/Homekit;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Linkapplications/shade/homekit/structures/Homekit;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}

public final class inkapplications/shade/homekit/structures/Homekit$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class inkapplications/shade/homekit/structures/HomekitAction {
public static final field Companion Linkapplications/shade/homekit/structures/HomekitAction$Companion;
public static final synthetic fun box-impl (Ljava/lang/String;)Linkapplications/shade/homekit/structures/HomekitAction;
public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
public fun equals (Ljava/lang/Object;)Z
public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
public final fun getKey ()Ljava/lang/String;
public fun hashCode ()I
public static fun hashCode-impl (Ljava/lang/String;)I
public fun toString ()Ljava/lang/String;
public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
public final synthetic fun unbox-impl ()Ljava/lang/String;
}

public final synthetic class inkapplications/shade/homekit/structures/HomekitAction$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Linkapplications/shade/homekit/structures/HomekitAction$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun deserialize-_n2LV64 (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/String;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public final fun serialize-gK-Otuo (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/String;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}

public final class inkapplications/shade/homekit/structures/HomekitAction$Companion {
public final fun getReset-_IGrqpU ()Ljava/lang/String;
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class inkapplications/shade/homekit/structures/HomekitStatus {
public static final field Companion Linkapplications/shade/homekit/structures/HomekitStatus$Companion;
public static final synthetic fun box-impl (Ljava/lang/String;)Linkapplications/shade/homekit/structures/HomekitStatus;
public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
public fun equals (Ljava/lang/Object;)Z
public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
public final fun getKey ()Ljava/lang/String;
public fun hashCode ()I
public static fun hashCode-impl (Ljava/lang/String;)I
public fun toString ()Ljava/lang/String;
public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
public final synthetic fun unbox-impl ()Ljava/lang/String;
}

public final synthetic class inkapplications/shade/homekit/structures/HomekitStatus$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Linkapplications/shade/homekit/structures/HomekitStatus$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun deserialize-5d-ibaU (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/String;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public final fun serialize-4oD0JvU (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/String;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}

public final class inkapplications/shade/homekit/structures/HomekitStatus$Companion {
public final fun getPaired-iTYh7UM ()Ljava/lang/String;
public final fun getPairing-iTYh7UM ()Ljava/lang/String;
public final fun getUnpaired-iTYh7UM ()Ljava/lang/String;
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

27 changes: 27 additions & 0 deletions homekit/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
id("library")
kotlin("plugin.serialization")
id("ink.publishing")
}

kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(libs.serialization.json)
implementation(projects.internals)
implementation(projects.serialization)
api(projects.structures)

api(libs.coroutines.core)
}
}

val commonTest by getting {
dependencies {
implementation(libs.test.core)
implementation(libs.test.annotations)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package inkapplications.shade.homekit

import inkapplications.shade.homekit.parameters.HomekitUpdateParameters
import inkapplications.shade.homekit.structures.Homekit
import inkapplications.shade.structures.ResourceId
import inkapplications.shade.structures.ResourceReference

/**
* Actions to get homekit resources in the hue system.
*/
interface HomekitControls {
/**
* Get the state of a single homekit resource.
*
* @param id The resource ID of the homekit resource to fetch data for.
*/
suspend fun getHomekit(id: ResourceId): Homekit

/**
* Get a list of homekit resources configured on the hue service.
*/
suspend fun listHomekit(): List<Homekit>

/**
* Invoke an action on the specified homekit resource.
*
* @param id The resource ID of the homekit resource to be updated
* @param parameters Data about the homekit resource to be updated
*/
suspend fun updateHomekit(id: ResourceId, parameters: HomekitUpdateParameters): ResourceReference
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package inkapplications.shade.homekit

import inkapplications.shade.homekit.parameters.HomekitUpdateParameters
import inkapplications.shade.homekit.structures.Homekit
import inkapplications.shade.internals.HueHttpClient
import inkapplications.shade.internals.getData
import inkapplications.shade.internals.putData
import inkapplications.shade.structures.ResourceId
import inkapplications.shade.structures.ResourceReference

/**
* Implements homekit controls via the hue client.
*/
internal class ShadeHomekit(
private val hueHttpClient: HueHttpClient,
) : HomekitControls {
override suspend fun getHomekit(id: ResourceId): Homekit {
return hueHttpClient.getData<List<Homekit>>("resource", "homekit", id.value).single()
}

override suspend fun listHomekit(): List<Homekit> {
return hueHttpClient.getData("resource", "homekit")
}

override suspend fun updateHomekit(
id: ResourceId,
parameters: HomekitUpdateParameters
): ResourceReference {
val response: List<ResourceReference> = hueHttpClient.putData(
body = parameters,
pathSegments = arrayOf("resource", "homekit", id.value),
)

return response.single()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package inkapplications.shade.homekit

import inkapplications.shade.internals.InternalsModule

/**
* Provides Access to homekit control services.
*/
class ShadeHomekitModule(
internalsModule: InternalsModule,
) {
val homekit: HomekitControls = ShadeHomekit(internalsModule.hueHttpClient)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package inkapplications.shade.homekit.parameters

import inkapplications.shade.homekit.structures.HomekitAction
import kotlinx.serialization.Serializable

/**
* Options for updating a homekit resource.
*/
@Serializable
data class HomekitUpdateParameters(
/**
* Action to perform on the homekit resource.
*/
val action: HomekitAction? = null,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package inkapplications.shade.homekit.structures

import inkapplications.shade.structures.ResourceId
import inkapplications.shade.structures.ResourceType
import kotlinx.serialization.Serializable

/**
* State of the homekit resource.
*/
@Serializable
data class Homekit(
/**
* Unique identifier representing a specific homekit resource instance.
*/
val id: ResourceId,

/**
* Type of the supported resources.
*/
val type: ResourceType = ResourceType.Homekit,

/**
* Status indicating whether homekit is paired, currently open for pairing, or unpaired.
*/
val status: HomekitStatus,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package inkapplications.shade.homekit.structures

import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline

/**
* Action that can be performed on a homekit resource.
*/
@JvmInline
@Serializable
value class HomekitAction(val key: String) {
override fun toString(): String = key

companion object {
/**
* Reset homekit.
*
* Removes all pairings and reset state and Bonjour service to
* factory settings.
*
* The Homekit will start functioning after approximately 10 seconds.
*/
val Reset = HomekitAction("homekit_reset")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package inkapplications.shade.homekit.structures

import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline

/**
* Status indicating whether homekit is paired, currently open for pairing, or unpaired.
*
* Transitions:
* - unpaired > pairing – pushlink button press or power cycle
* - pairing > paired – through HAP
* - pairing > unpaired – 10 minutes
* - paired > unpaired – homekit reset
*/
@JvmInline
@Serializable
value class HomekitStatus(val key: String) {
override fun toString(): String = key

companion object {
/**
* Homekit is paired with the bridge.
*/
val Paired = HomekitStatus("paired")

/**
* Homekit is currently in pairing mode.
*/
val Pairing = HomekitStatus("pairing")

/**
* Homekit is not paired with the bridge.
*/
val Unpaired = HomekitStatus("unpaired")
}
}
Loading