├── docs
├── CNAME
├── reference
│ └── latest
│ │ ├── images
│ │ ├── homepage.svg
│ │ ├── copy-icon.svg
│ │ ├── footer-go-to-link.svg
│ │ ├── copy-successful-icon.svg
│ │ ├── go-to-top-icon.svg
│ │ ├── arrow_down.svg
│ │ ├── burger.svg
│ │ ├── nav-icons
│ │ │ ├── exception-class.svg
│ │ │ ├── enum.svg
│ │ │ ├── field-value.svg
│ │ │ ├── interface.svg
│ │ │ ├── field-variable.svg
│ │ │ ├── interface-kotlin.svg
│ │ │ ├── enum-kotlin.svg
│ │ │ ├── typealias-kotlin.svg
│ │ │ ├── class.svg
│ │ │ ├── object.svg
│ │ │ ├── class-kotlin.svg
│ │ │ ├── function.svg
│ │ │ └── abstract-class.svg
│ │ ├── logo-icon.svg
│ │ ├── anchor-copy-button.svg
│ │ └── theme-toggle.svg
│ │ ├── ui-kit
│ │ └── assets
│ │ │ ├── arrow-down.svg
│ │ │ ├── homepage.svg
│ │ │ ├── checkbox-off.svg
│ │ │ ├── placeholder.svg
│ │ │ ├── checkbox-on.svg
│ │ │ ├── burger.svg
│ │ │ ├── cross.svg
│ │ │ ├── exception-class.svg
│ │ │ ├── enum.svg
│ │ │ ├── field-value.svg
│ │ │ ├── interface.svg
│ │ │ ├── field-variable.svg
│ │ │ ├── filter.svg
│ │ │ ├── interface-kotlin.svg
│ │ │ ├── enum-kotlin.svg
│ │ │ ├── typealias-kotlin.svg
│ │ │ ├── theme-toggle.svg
│ │ │ ├── class.svg
│ │ │ ├── object.svg
│ │ │ ├── class-kotlin.svg
│ │ │ ├── function.svg
│ │ │ └── abstract-class.svg
│ │ ├── styles
│ │ ├── logo-styles.css
│ │ └── jetbrains-mono.css
│ │ └── package-list
└── css
│ ├── dark-1.1.css
│ ├── reset.css
│ ├── main-2.0.css
│ ├── atom-one-dark.css
│ └── atom-one-light.css
├── .github
├── FUNDING.yml
├── workflows
│ ├── pull_requests.yml
│ └── pushes.yml
├── ISSUE_TEMPLATE
│ ├── question.md
│ ├── feature-request.md
│ ├── device-bugs.md
│ ├── api-bugs.md
│ └── cli-bugs.md
└── dependabot.yml
├── .gitignore
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── shade-cli
├── serialization
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── serialization
│ │ ├── HueError.kt
│ │ ├── MillisecondDurationSerializer.kt
│ │ ├── FractionalPercentageSerializer.kt
│ │ ├── WholePercentageSerializer.kt
│ │ ├── ExplicitMiredSerializer.kt
│ │ ├── MiredSerializer.kt
│ │ └── DelegateSerializer.kt
└── build.gradle.kts
├── core
├── src
│ └── jvmMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── core
│ │ └── JvmExtensions.kt
├── build.gradle.kts
└── api
│ └── core.api
├── discover
├── src
│ ├── jsMain
│ │ └── kotlin
│ │ │ └── inkapplications
│ │ │ └── shade
│ │ │ └── discover
│ │ │ └── PlatformModule.kt
│ ├── iosMain
│ │ └── kotlin
│ │ │ └── inkapplications
│ │ │ └── shade
│ │ │ └── discover
│ │ │ └── PlatformModule.kt
│ ├── jvmMain
│ │ └── kotlin
│ │ │ └── inkapplications
│ │ │ └── shade
│ │ │ └── discover
│ │ │ └── PlatformModule.kt
│ ├── nativeMain
│ │ └── kotlin
│ │ │ └── inkapplications
│ │ │ └── shade
│ │ │ └── discover
│ │ │ └── PlatformModule.kt
│ ├── windowsMain
│ │ └── kotlin
│ │ │ └── inkapplications
│ │ │ └── shade
│ │ │ └── discover
│ │ │ └── PlatformModule.kt
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── discover
│ │ ├── structures
│ │ ├── BridgeId.kt
│ │ └── Bridge.kt
│ │ ├── BridgeDiscovery.kt
│ │ ├── PlatformModule.kt
│ │ ├── DiscoverModule.kt
│ │ └── KtorDiscovery.kt
└── build.gradle.kts
├── zones
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── zones
│ │ ├── ShadeZonesModule.kt
│ │ ├── parameters
│ │ ├── ZoneCreateParameters.kt
│ │ └── ZoneUpdateParameters.kt
│ │ └── structures
│ │ └── Zone.kt
└── build.gradle.kts
├── lights
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── lights
│ │ ├── structures
│ │ ├── ColorPalette.kt
│ │ ├── GradientPoint.kt
│ │ ├── LightSignaling.kt
│ │ ├── AlertInfo.kt
│ │ ├── ColorTemperaturePalette.kt
│ │ ├── ColorValue.kt
│ │ ├── DimmingValue.kt
│ │ ├── LightingEffectInfo.kt
│ │ ├── TimedLightingEffectInfo.kt
│ │ ├── GradientMode.kt
│ │ ├── GradientValue.kt
│ │ ├── ColorTemperatureValue.kt
│ │ ├── Gradient.kt
│ │ ├── LightSignalStatus.kt
│ │ ├── DimmingInfo.kt
│ │ ├── ColorInfo.kt
│ │ ├── AlertEffectType.kt
│ │ ├── LightMode.kt
│ │ ├── Gamut.kt
│ │ ├── LightSignal.kt
│ │ ├── TimedLightEffect.kt
│ │ ├── DynamicsStatus.kt
│ │ ├── LightEffect.kt
│ │ ├── LightDynamics.kt
│ │ ├── ColorTemperatureRange.kt
│ │ ├── GamutType.kt
│ │ └── Chromaticity.kt
│ │ ├── parameters
│ │ ├── AlertParameters.kt
│ │ ├── ColorParameters.kt
│ │ ├── EffectsParameters.kt
│ │ ├── ColorTemperatureParameters.kt
│ │ ├── DimmingParameters.kt
│ │ ├── GradientParameters.kt
│ │ ├── ColorTemperatureDeltaParameters.kt
│ │ ├── TimedEffectsParameters.kt
│ │ ├── DimmingDeltaParameters.kt
│ │ ├── DeltaAction.kt
│ │ └── DynamicsParameters.kt
│ │ ├── events
│ │ ├── DimmingInfoEvent.kt
│ │ ├── ColorInfoEvent.kt
│ │ ├── ColorTemperatureInfoEvent.kt
│ │ └── LightEvent.kt
│ │ ├── ShadeLightsModule.kt
│ │ ├── LightControls.kt
│ │ └── ShadeLights.kt
└── build.gradle.kts
├── cli
├── src
│ └── main
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── cli
│ │ ├── scenes
│ │ ├── Assertions.kt
│ │ ├── ListScenesCommand.kt
│ │ ├── GetSceneCommand.kt
│ │ ├── DeleteSceneCommand.kt
│ │ └── SceneOutput.kt
│ │ ├── rooms
│ │ ├── ListRoomsCommand.kt
│ │ ├── RoomOutput.kt
│ │ ├── DeleteRoomCommand.kt
│ │ ├── GetRoomCommand.kt
│ │ └── CreateRoomCommand.kt
│ │ ├── zones
│ │ ├── ListZonesCommand.kt
│ │ ├── GetZoneCommand.kt
│ │ ├── ZoneOutput.kt
│ │ ├── DeleteRoomCommand.kt
│ │ └── CreateZoneCommand.kt
│ │ ├── lights
│ │ ├── ListLightsCommand.kt
│ │ ├── GetLightCommand.kt
│ │ └── LightOutput.kt
│ │ ├── devices
│ │ ├── ListDevicesCommand.kt
│ │ ├── DeleteDeviceCommand.kt
│ │ ├── GetDeviceCommand.kt
│ │ ├── IdentifyDeviceCommand.kt
│ │ ├── DeviceOutput.kt
│ │ └── UpdateDeviceCommand.kt
│ │ ├── groupedlights
│ │ ├── ListGroupedLightsCommand.kt
│ │ ├── GroupedLightsOutput.kt
│ │ └── GetGroupedLightCommand.kt
│ │ ├── connection
│ │ └── DiscoverCommand.kt
│ │ ├── resources
│ │ └── ListResourcesCommand.kt
│ │ └── AuthorizedShadeCommand.kt
└── build.gradle.kts
├── structures
├── src
│ ├── commonMain
│ │ └── kotlin
│ │ │ └── inkapplications
│ │ │ └── shade
│ │ │ └── structures
│ │ │ ├── PowerValue.kt
│ │ │ ├── parameters
│ │ │ └── PowerParameters.kt
│ │ │ ├── PowerInfo.kt
│ │ │ ├── ResourceId.kt
│ │ │ ├── UnknownEvent.kt
│ │ │ ├── SegmentMetadata.kt
│ │ │ ├── AuthToken.kt
│ │ │ ├── ResourceReference.kt
│ │ │ ├── SegmentMetadataUpdate.kt
│ │ │ ├── VersionString.kt
│ │ │ ├── UndocumentedApi.kt
│ │ │ ├── HueConfigurationContainer.kt
│ │ │ └── InMemoryConfigurationContainer.kt
│ └── jvmTest
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── structures
│ │ └── VersionStringTest.kt
└── build.gradle.kts
├── scenes
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── scenes
│ │ ├── ShadeScenesModule.kt
│ │ ├── structures
│ │ ├── SceneRecall.kt
│ │ ├── SceneActionReference.kt
│ │ ├── SceneMetadata.kt
│ │ ├── SceneRecallStatus.kt
│ │ ├── SceneRecallAction.kt
│ │ └── ScenePalette.kt
│ │ └── parameters
│ │ ├── SceneUpdateParameters.kt
│ │ └── SceneCreateParameters.kt
└── build.gradle.kts
├── rooms
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── rooms
│ │ ├── ShadeRoomsModule.kt
│ │ ├── parameters
│ │ ├── RoomCreateParameters.kt
│ │ └── RoomUpdateParameters.kt
│ │ ├── structures
│ │ └── Room.kt
│ │ └── RoomControls.kt
└── build.gradle.kts
├── devices
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── devices
│ │ ├── ShadeDevicesModule.kt
│ │ ├── structures
│ │ ├── ModelId.kt
│ │ ├── HardwarePlatformType.kt
│ │ ├── ProductMetadata.kt
│ │ ├── Device.kt
│ │ └── ProductData.kt
│ │ └── parameters
│ │ ├── IdentifyParameters.kt
│ │ ├── DeviceMetadataParameters.kt
│ │ └── UpdateDeviceParameters.kt
└── build.gradle.kts
├── resources
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── resources
│ │ ├── ResourceControls.kt
│ │ ├── ShadeResourcesModule.kt
│ │ ├── ShadeResources.kt
│ │ └── structures
│ │ └── Resource.kt
└── build.gradle.kts
├── events
├── src
│ ├── jvmMain
│ │ └── kotlin
│ │ │ └── shade
│ │ │ └── events
│ │ │ ├── EventsModule.kt
│ │ │ └── ShadeEvents.kt
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── events
│ │ ├── Events.kt
│ │ ├── EventSerializerContainer.kt
│ │ └── EventsModule.kt
├── build.gradle.kts
└── api
│ └── events.api
├── settings.gradle.kts
├── auth
├── src
│ ├── commonMain
│ │ └── kotlin
│ │ │ └── inkapplications
│ │ │ └── shade
│ │ │ └── auth
│ │ │ ├── structures
│ │ │ ├── AuthRequest.kt
│ │ │ └── AppId.kt
│ │ │ ├── AuthModule.kt
│ │ │ └── BridgeAuth.kt
│ └── jvmTest
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── auth
│ │ └── structures
│ │ └── AppIdSerializerTest.kt
└── build.gradle.kts
├── grouped-lights
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── groupedlights
│ │ ├── structures
│ │ ├── GroupDimmingInfo.kt
│ │ └── GroupedLight.kt
│ │ ├── ShadeGroupedLightsModule.kt
│ │ ├── ShadeGroupedLights.kt
│ │ ├── GroupedLightControls.kt
│ │ └── events
│ │ └── GroupedLightEvent.kt
└── build.gradle.kts
├── internals
└── src
│ ├── commonMain
│ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── internals
│ │ ├── SseClient.kt
│ │ ├── BaseUrl.kt
│ │ ├── PlatformModule.kt
│ │ ├── HueStubClient.kt
│ │ ├── DummyConfigurationContainer.kt
│ │ ├── InternalsModule.kt
│ │ └── CachedProperty.kt
│ ├── jsMain
│ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── internals
│ │ └── PlatformModule.kt
│ ├── iosMain
│ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── internals
│ │ └── PlatformModule.kt
│ ├── nativeMain
│ └── kotlin
│ │ └── inkapplications
│ │ └── shade
│ │ └── internals
│ │ └── PlatformModule.kt
│ └── windowsMain
│ └── kotlin
│ └── inkapplications
│ └── shade
│ └── internals
│ └── PlatformModule.kt
├── LICENSE
└── README.md
/docs/CNAME:
--------------------------------------------------------------------------------
1 | shade.lighting
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: ReneeVandervelde
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | .gradle/
3 | .shade-config.properties
4 | .kotlin/
5 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | version=2.0-SNAPSHOT
2 | group=com.inkapplications.shade
3 | org.gradle.jvmargs=-Xmx4g
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/InkApplications/Shade/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/shade-cli:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ./gradlew cli:installDist --quiet && \
4 | cli/build/install/shade/bin/shade "$@"
5 |
--------------------------------------------------------------------------------
/.github/workflows/pull_requests.yml:
--------------------------------------------------------------------------------
1 | on: [pull_request]
2 | jobs:
3 | tests:
4 | uses: inkapplications/.github/.github/workflows/kmp-checks.yml@1.2.0
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Ask a usage or documentation question.
4 | title: ''
5 | labels: question
6 | assignees: ''
7 | ---
8 |
9 | ### Question:
10 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/homepage.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/arrow-down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/homepage.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/checkbox-off.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request / Suggestion
3 | about: Request support for a new feature or change to Shade's API.
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 | ---
8 |
9 | ### Proposed Change:
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/copy-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/footer-go-to-link.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "gradle"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | - package-ecosystem: "github-actions"
8 | directory: "/"
9 | schedule:
10 | interval: "weekly"
11 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/placeholder.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/copy-successful-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/serialization/src/commonMain/kotlin/inkapplications/shade/serialization/HueError.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.serialization
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Error from the hue API
7 | */
8 | @Serializable
9 | data class HueError(
10 | val description: String,
11 | )
12 |
--------------------------------------------------------------------------------
/core/src/jvmMain/kotlin/inkapplications/shade/core/JvmExtensions.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.core
2 |
3 | import inkapplications.shade.events.Events
4 | import inkapplications.shade.structures.UndocumentedApi
5 | import shade.events.events
6 |
7 | @UndocumentedApi
8 | val Shade.events: Events get() = eventsModule.events
9 |
--------------------------------------------------------------------------------
/docs/reference/latest/styles/logo-styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 | */
4 |
5 | :root {
6 | --dokka-logo-image-url: url('../images/logo-icon.svg');
7 | --dokka-logo-height: 28px;
8 | --dokka-logo-width: 28px;
9 | }
10 |
--------------------------------------------------------------------------------
/discover/src/jsMain/kotlin/inkapplications/shade/discover/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import io.ktor.client.engine.*
4 | import io.ktor.client.engine.js.*
5 |
6 | internal actual class PlatformModule actual constructor() {
7 | actual fun createEngine(): HttpClientEngineFactory<*> = Js
8 | }
9 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/go-to-top-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/discover/src/iosMain/kotlin/inkapplications/shade/discover/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import io.ktor.client.engine.*
4 | import io.ktor.client.engine.darwin.*
5 |
6 | internal actual class PlatformModule actual constructor() {
7 | actual fun createEngine(): HttpClientEngineFactory<*> = Darwin
8 | }
9 |
--------------------------------------------------------------------------------
/discover/src/jvmMain/kotlin/inkapplications/shade/discover/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import io.ktor.client.engine.*
4 | import io.ktor.client.engine.okhttp.*
5 |
6 | internal actual class PlatformModule actual constructor() {
7 | actual fun createEngine(): HttpClientEngineFactory<*> = OkHttp
8 | }
9 |
--------------------------------------------------------------------------------
/discover/src/nativeMain/kotlin/inkapplications/shade/discover/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import io.ktor.client.engine.*
4 | import io.ktor.client.engine.cio.*
5 |
6 | internal actual class PlatformModule actual constructor() {
7 | actual fun createEngine(): HttpClientEngineFactory<*> = CIO
8 | }
9 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/arrow_down.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/discover/src/windowsMain/kotlin/inkapplications/shade/discover/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import io.ktor.client.engine.*
4 | import io.ktor.client.engine.winhttp.*
5 |
6 | internal actual class PlatformModule actual constructor() {
7 | actual fun createEngine(): HttpClientEngineFactory<*> = WinHttp
8 | }
9 |
--------------------------------------------------------------------------------
/zones/src/commonMain/kotlin/inkapplications/shade/zones/ShadeZonesModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.zones
2 |
3 | import inkapplications.shade.internals.InternalsModule
4 |
5 | class ShadeZonesModule(
6 | internalsModule: InternalsModule,
7 | ) {
8 | val zones: ZoneControls = ShadeZones(internalsModule.hueHttpClient)
9 | }
10 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/checkbox-on.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/ColorPalette.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Color/brightness pair reference.
7 | */
8 | @Serializable
9 | data class ColorPalette(
10 | val color: ColorValue,
11 | val dimming: DimmingValue,
12 | )
13 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/scenes/Assertions.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.scenes
2 |
3 | /**
4 | * Assert that two lists are the same size.
5 | */
6 | fun assertSameSize(expected: List<*>?, actual: List<*>?, message: String) {
7 | if (expected?.size != actual?.size) {
8 | throw IllegalArgumentException(message)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/burger.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/cross.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/PowerValue.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Describes the power state of a device.
7 | */
8 | @Serializable
9 | data class PowerValue(
10 | /**
11 | * Whether the device is currently powered on.
12 | */
13 | val on: Boolean,
14 | )
15 |
--------------------------------------------------------------------------------
/discover/src/commonMain/kotlin/inkapplications/shade/discover/structures/BridgeId.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Wraps a bridge ID value.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class BridgeId(val value: String) {
12 | override fun toString(): String = value
13 | }
14 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/ShadeScenesModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes
2 |
3 | import inkapplications.shade.internals.InternalsModule
4 |
5 | /**
6 | * Provides Access to scene services.
7 | */
8 | class ShadeScenesModule(
9 | internalsModule: InternalsModule,
10 | ) {
11 | val scenes: SceneControls = ShadeScenes(internalsModule.hueHttpClient)
12 | }
13 |
--------------------------------------------------------------------------------
/rooms/src/commonMain/kotlin/inkapplications/shade/rooms/ShadeRoomsModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.rooms
2 |
3 | import inkapplications.shade.internals.InternalsModule
4 |
5 | /**
6 | * Provides Access to room control services.
7 | */
8 | class ShadeRoomsModule(
9 | internalsModule: InternalsModule,
10 | ) {
11 | val rooms: RoomControls = ShadeRooms(internalsModule.hueHttpClient)
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/discover/src/commonMain/kotlin/inkapplications/shade/discover/BridgeDiscovery.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import inkapplications.shade.discover.structures.Bridge
4 |
5 | /**
6 | * Hue bridge discovery functions
7 | */
8 | interface BridgeDiscovery {
9 | /**
10 | * Get a list of Hue Bridges available on the network.
11 | */
12 | suspend fun getDevices(): List
13 | }
14 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/parameters/PowerParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures.parameters
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Power settings for a device
7 | */
8 | @Serializable
9 | data class PowerParameters(
10 | /**
11 | * Simple on/off state for the device
12 | */
13 | val on: Boolean? = null,
14 | )
15 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/ShadeDevicesModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices
2 |
3 | import inkapplications.shade.internals.InternalsModule
4 |
5 | /**
6 | * Module for accessing Device information.
7 | */
8 | class ShadeDevicesModule(
9 | internalsModule: InternalsModule,
10 | ) {
11 | val devices: DeviceControls = ShadeDevices(internalsModule.hueHttpClient)
12 | }
13 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/structures/ModelId.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Identifier for a specific model of hardware.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class ModelId(val value: String) {
12 | override fun toString(): String = value
13 | }
14 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/PowerInfo.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Describes the power state of a device and associated capabilities.
7 | */
8 | @Serializable
9 | data class PowerInfo(
10 | /**
11 | * Whether the device is currently powered on.
12 | */
13 | val on: Boolean,
14 | )
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/device-bugs.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Device Support
3 | about: Problems connecting or controlling a specific Hue device.
4 | title: ''
5 | labels: bug, device support
6 | assignees: ''
7 | ---
8 |
9 | ### What Device is Having Issues?
10 | - Product Name: [Color Bulb, Light Strip, etc.]
11 | - Product Part Number: [046677548483 / any ID that isn't the serial number]
12 |
13 |
14 | ### What's the Issue?
15 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/GradientPoint.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Contains information on a single gradient point.
8 | */
9 | @Serializable
10 | data class GradientPoint(
11 | @SerialName("color")
12 | val colorValue: ColorValue,
13 | )
14 |
--------------------------------------------------------------------------------
/resources/src/commonMain/kotlin/inkapplications/shade/resources/ResourceControls.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.resources
2 |
3 | import inkapplications.shade.resources.structures.Resource
4 |
5 | /**
6 | * Actions for API resources on the hue bridge
7 | */
8 | interface ResourceControls {
9 | /**
10 | * Retrieve all known API resources
11 | */
12 | suspend fun listResources(): List
13 | }
14 |
--------------------------------------------------------------------------------
/cli/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | application
3 | kotlin("jvm")
4 | }
5 |
6 | application {
7 | applicationName = "shade"
8 | mainClass.set("inkapplications.shade.cli.MainKt")
9 | }
10 |
11 | dependencies {
12 | implementation(libs.coroutines.core)
13 | implementation("com.github.ajalt.clikt:clikt:4.4.0")
14 | implementation("org.slf4j:slf4j-nop:2.0.16")
15 | implementation(projects.core)
16 | }
17 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/parameters/IdentifyParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.parameters
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Internal object used to trigger an identify event.
7 | */
8 | @Serializable
9 | internal class IdentifyParameters private constructor(
10 | val action: String,
11 | ) {
12 | constructor(): this("identify")
13 | }
14 |
--------------------------------------------------------------------------------
/discover/src/commonMain/kotlin/inkapplications/shade/discover/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import io.ktor.client.engine.*
4 |
5 | /**
6 | * Provides platform-specific dependencies for the SDK.
7 | */
8 | internal expect class PlatformModule() {
9 | /**
10 | * Create Ktor http engine based on the platform.
11 | */
12 | fun createEngine(): HttpClientEngineFactory<*>
13 | }
14 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/ResourceId.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Unique identifier representing a specific resource instance.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class ResourceId(val value: String) {
12 | override fun toString(): String = value
13 | }
14 |
--------------------------------------------------------------------------------
/resources/src/commonMain/kotlin/inkapplications/shade/resources/ShadeResourcesModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.resources
2 |
3 | import inkapplications.shade.internals.InternalsModule
4 |
5 | /**
6 | * Module for accessing Resource information.
7 | */
8 | class ShadeResourcesModule(
9 | internalsModule: InternalsModule,
10 | ) {
11 | val resources: ResourceControls = ShadeResources(internalsModule.hueHttpClient)
12 | }
13 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/structures/HardwarePlatformType.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Hardware type as identified by Manufacturer.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class HardwarePlatformType(val value: String) {
12 | override fun toString(): String = value
13 | }
14 |
--------------------------------------------------------------------------------
/events/src/jvmMain/kotlin/shade/events/EventsModule.kt:
--------------------------------------------------------------------------------
1 | package shade.events
2 |
3 | import inkapplications.shade.events.Events
4 | import inkapplications.shade.events.EventsModule
5 | import inkapplications.shade.structures.UndocumentedApi
6 |
7 | @UndocumentedApi
8 | val EventsModule.events: Events
9 | get() = ShadeEvents(
10 | sseClient = internalsModule.platformModule.sseClient,
11 | eventDeserializer = eventDeserializer,
12 | )
13 |
--------------------------------------------------------------------------------
/events/src/commonMain/kotlin/inkapplications/shade/events/Events.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.events
2 |
3 | import inkapplications.shade.structures.UndocumentedApi
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | /**
7 | * Event streams available from the shade client.
8 | */
9 | @UndocumentedApi
10 | interface Events {
11 | /**
12 | * Events emitted by the hue bridge.
13 | */
14 | fun bridgeEvents(): Flow>
15 | }
16 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/LightSignaling.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Signaling properties for a light.
7 | */
8 | @Serializable
9 | data class LightSignaling(
10 | /**
11 | * Indicates status of active signal. Not available when inactive.
12 | */
13 | val status: LightSignalStatus? = null,
14 | )
15 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "shade"
2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
3 |
4 | include("auth")
5 | include("cli")
6 | include("core")
7 | include("devices")
8 | include("discover")
9 | include("events")
10 | include("grouped-lights")
11 | include("internals")
12 | include("lights")
13 | include("resources")
14 | include("rooms")
15 | include("scenes")
16 | include("serialization")
17 | include("structures")
18 | include("zones")
19 |
--------------------------------------------------------------------------------
/docs/css/dark-1.1.css:
--------------------------------------------------------------------------------
1 | html {
2 | background-color: #212121;
3 | color: #fff;
4 | }
5 | h1,
6 | h2,
7 | h3
8 | {
9 | color: #fff;
10 | }
11 |
12 | a,
13 | a:visited,
14 | a:hover
15 | {
16 | color: #fff;
17 | }
18 |
19 | code,
20 | .hljs {
21 | background-color: #292929;
22 | }
23 |
24 | .only-light {
25 | display: none;
26 | }
27 | .only-dark {
28 | display: initial;
29 | }
30 |
31 | .hero > article {
32 | background-color: #292929;
33 | }
34 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/AlertParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.lights.structures.AlertEffectType
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Change alert state of the light
8 | */
9 | @Serializable
10 | data class AlertParameters(
11 | /**
12 | * Alert effect to set on the light.
13 | */
14 | val action: AlertEffectType,
15 | )
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/api-bugs.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: SDK Bug Report
3 | about: Unexpected problems or exceptions in the SDK
4 | title: ''
5 | labels: bug, SDK
6 | assignees: ''
7 | ---
8 |
9 | ### Shade Info:
10 | - Version: [1.0.0, etc]
11 |
12 | ### How to Reproduce the Issue:
13 |
14 | Example:
15 | ```kotlin
16 | fun main() {
17 | val shade = Shade()
18 |
19 | // TODO: Show what caused the issue.
20 | }
21 | ```
22 |
23 | Expected behavior:
24 |
25 | Actual behavior:
26 |
27 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/UnknownEvent.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | /**
4 | * An event that was sent through the events api, but not deserialized by
5 | * any module.
6 | */
7 | @UndocumentedApi
8 | data class UnknownEvent(
9 | /**
10 | * Designated type for the data.
11 | */
12 | val type: String,
13 |
14 | /**
15 | * Raw JSON string of the event object.
16 | */
17 | val json: String,
18 | )
19 |
--------------------------------------------------------------------------------
/auth/src/commonMain/kotlin/inkapplications/shade/auth/structures/AuthRequest.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.auth.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Request used when authenticating with the hue API
8 | */
9 | @Serializable
10 | internal class AuthRequest(
11 | @SerialName("devicetype")
12 | val appId: AppId,
13 | @SerialName("generateclientkey")
14 | val generateClientKey: Boolean,
15 | )
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/cli-bugs.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: CLI Bug Report
3 | about: Unexpected problems controlling lights on the command line.
4 | title: ''
5 | labels: bug, CLI
6 | assignees: ''
7 | ---
8 |
9 | ### Shade Info:
10 | - Version: [1.0.0, etc]
11 | - OS: [Mac/Windows/Linux]
12 |
13 | ### How to Reproduce the Issue:
14 |
15 | Example command:
16 | ```shell
17 | [TODO - Show the commands you used when seeing this issue.]
18 | ```
19 |
20 | Expected behavior:
21 |
22 | Actual behavior:
23 |
24 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/AlertInfo.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Information on Alert effects for a light
8 | */
9 | @Serializable
10 | data class AlertInfo(
11 | /**
12 | * Alert effects that the light supports.
13 | */
14 | @SerialName("action_values")
15 | val actionValues: List
16 | )
17 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/ColorTemperaturePalette.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Color temperature/brightness pair reference.
8 | */
9 | @Serializable
10 | data class ColorTemperaturePalette(
11 | @SerialName("color_temperature")
12 | val colorTemperature: ColorTemperatureValue,
13 | val dimming: DimmingValue,
14 | )
15 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/burger.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/rooms/ListRoomsCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.rooms
2 |
3 | import inkapplications.shade.cli.AuthorizedShadeCommand
4 |
5 | object ListRoomsCommand: AuthorizedShadeCommand(
6 | help = "Get all of the rooms configured on the Hue bridge"
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val rooms = shade.rooms.listRooms()
10 |
11 | logger.debug("Got Rooms: $rooms")
12 | rooms.forEach(::echoRoom)
13 |
14 | return 0
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/zones/ListZonesCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.zones
2 |
3 | import inkapplications.shade.cli.AuthorizedShadeCommand
4 |
5 | object ListZonesCommand: AuthorizedShadeCommand(
6 | help = "Get all of the zones configured on the Hue bridge"
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val zones = shade.zones.listZones()
10 |
11 | logger.debug("Got Zones: $zones")
12 | zones.forEach(::echoZone)
13 |
14 | return 0
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/lights/ListLightsCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.lights
2 |
3 | import inkapplications.shade.cli.AuthorizedShadeCommand
4 |
5 | object ListLightsCommand: AuthorizedShadeCommand(
6 | help = "Get all lights connected to the bridge"
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val lights = shade.lights.listLights()
10 |
11 | logger.debug("Got Lights: $lights")
12 | lights.forEach { echoLight(it) }
13 |
14 | return 0
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/SegmentMetadata.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Configuration data for a segment of devices, such as a room or zone.
7 | */
8 | @Serializable
9 | data class SegmentMetadata(
10 | /**
11 | * Category of type/purpose of the segment
12 | */
13 | val archetype: SegmentArchetype,
14 |
15 | /**
16 | * Human readable name of the room.
17 | */
18 | val name: String,
19 | )
20 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/scenes/ListScenesCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.scenes
2 |
3 | import inkapplications.shade.cli.AuthorizedShadeCommand
4 |
5 | object ListScenesCommand: AuthorizedShadeCommand(
6 | help = "Get all of the scenes configured on the Hue bridge",
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val scenes = shade.scenes.listScenes()
10 |
11 | logger.debug("Got Scenes: $scenes")
12 | scenes.forEach(::echoScene)
13 |
14 | return 0
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/devices/ListDevicesCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.devices
2 |
3 | import inkapplications.shade.cli.AuthorizedShadeCommand
4 |
5 | object ListDevicesCommand: AuthorizedShadeCommand(
6 | help = "Get all of the devices configured on the hue bridge",
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val devices = shade.devices.listDevices()
10 |
11 | logger.debug("Got Devices: $devices")
12 | devices.forEach(::echoDevice)
13 |
14 | return 0
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/exception-class.svg:
--------------------------------------------------------------------------------
1 |
4 |
10 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/ColorValue.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import com.github.ajalt.colormath.Color
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Information about a light's color.
9 | */
10 | @Serializable
11 | data class ColorValue(
12 | /**
13 | * Current color of the light
14 | */
15 | @SerialName("xy")
16 | @Serializable(with = Chromaticity.ColorSerializer::class)
17 | val color: Color,
18 | )
19 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/exception-class.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/AuthToken.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Wraps an API key used for authenticating with the Hue API
8 | */
9 | @Serializable
10 | data class AuthToken(
11 | @SerialName("username")
12 | val applicationKey: String,
13 | @SerialName("clientkey")
14 | val clientKey: String? = null,
15 | ) {
16 | override fun toString(): String {
17 | return "AuthToken{}"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/groupedlights/ListGroupedLightsCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.groupedlights
2 |
3 | import inkapplications.shade.cli.AuthorizedShadeCommand
4 |
5 | object ListGroupedLightsCommand: AuthorizedShadeCommand(
6 | help = "Get all grouped lights configured on the Hue bridge",
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val groups = shade.groupedLights.listGroups()
10 |
11 | logger.debug("Got Groups: $groups")
12 | groups.forEach { echoGroup(it) }
13 |
14 | return 0
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/structures/ProductMetadata.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Configured metadata for a specific [Device].
7 | */
8 | @Serializable
9 | data class ProductMetadata(
10 | /**
11 | * Human readable name for the device.
12 | */
13 | val name: String,
14 |
15 | /**
16 | * User-configured archetype for the device or default given by the
17 | * manufacturer.
18 | */
19 | val archetype: ProductArchetype,
20 | )
21 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/DimmingValue.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import inkapplications.shade.serialization.WholePercentageSerializer
4 | import inkapplications.spondee.scalar.Percentage
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Info about a light's dimming status.
9 | */
10 | @Serializable
11 | data class DimmingValue(
12 | /**
13 | * Current brightness value.
14 | */
15 | @Serializable(with = WholePercentageSerializer::class)
16 | val brightness: Percentage,
17 | )
18 |
--------------------------------------------------------------------------------
/auth/src/commonMain/kotlin/inkapplications/shade/auth/AuthModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.auth
2 |
3 | import inkapplications.shade.internals.InternalsModule
4 | import kimchi.logger.KimchiLogger
5 |
6 | /**
7 | * Provides access to authorization services
8 | */
9 | class AuthModule(
10 | internalsModule: InternalsModule,
11 | logger: KimchiLogger,
12 | ) {
13 | val bridgeAuth: BridgeAuth = ShadeBridgeAuth(
14 | client = internalsModule.hueHttpClient,
15 | configurationContainer = internalsModule.configurationContainer,
16 | logger = logger,
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/enum.svg:
--------------------------------------------------------------------------------
1 |
4 |
10 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/connection/DiscoverCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.connection
2 |
3 | import inkapplications.shade.cli.ShadeCommand
4 |
5 | object DiscoverCommand: ShadeCommand(
6 | help = "Discover hue bridges on the network",
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val devices = shade.onlineDiscovery.getDevices()
10 |
11 | devices.forEach {
12 | echo("${it.id}:")
13 | echo(" ip: ${it.localIp}")
14 | echo(" port: ${it.port}")
15 | }
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/field-value.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/interface.svg:
--------------------------------------------------------------------------------
1 |
4 |
10 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/enum.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/field-value.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/resources/src/commonMain/kotlin/inkapplications/shade/resources/ShadeResources.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.resources
2 |
3 | import inkapplications.shade.internals.HueHttpClient
4 | import inkapplications.shade.internals.getData
5 | import inkapplications.shade.resources.structures.Resource
6 |
7 | /**
8 | * Implements Resource Controls using the hue client.
9 | */
10 | internal class ShadeResources(
11 | private val hueHttpClient: HueHttpClient,
12 | ): ResourceControls {
13 | override suspend fun listResources(): List {
14 | return hueHttpClient.getData("resource")
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/structures/SceneRecall.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.structures
2 |
3 | import inkapplications.shade.lights.structures.DimmingValue
4 | import inkapplications.shade.serialization.MillisecondDurationSerializer
5 | import kotlinx.serialization.Serializable
6 | import kotlin.time.Duration
7 |
8 | @Serializable
9 | data class SceneRecall(
10 | val action: SceneRecallAction,
11 | val status: SceneRecallStatus,
12 | @Serializable(with = MillisecondDurationSerializer::class)
13 | val duration: Duration,
14 | val dimming: DimmingValue,
15 | )
16 |
--------------------------------------------------------------------------------
/serialization/src/commonMain/kotlin/inkapplications/shade/serialization/MillisecondDurationSerializer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.serialization
2 |
3 | import kotlinx.serialization.builtins.serializer
4 | import kotlin.time.Duration
5 | import kotlin.time.Duration.Companion.milliseconds
6 |
7 | /**
8 | * Serialize a Duration as a Long in milliseconds.
9 | */
10 | object MillisecondDurationSerializer: DelegateSerializer(Long.serializer()) {
11 | override fun serialize(data: Duration): Long = data.inWholeMilliseconds
12 | override fun deserialize(data: Long): Duration = data.milliseconds
13 | }
14 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/zones/GetZoneCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.zones
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object GetZoneCommand: AuthorizedShadeCommand(
8 | help = "Get information for a specific Zone"
9 | ) {
10 | private val id by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val zone = shade.zones.getZone(id)
14 |
15 | echoZone(zone)
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/LightingEffectInfo.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Basic feature containing effect properties.
8 | */
9 | @Serializable
10 | data class LightingEffectInfo(
11 | /**
12 | * Current lighting effect
13 | */
14 | val status: LightEffect,
15 |
16 | /**
17 | * List of supported lighting effects on this device.
18 | */
19 | @SerialName("effect_values")
20 | val values: List,
21 | )
22 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/structures/SceneActionReference.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.structures
2 |
3 | import inkapplications.shade.structures.ResourceReference
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Action to be applied to a specified resource for a scene.
8 | */
9 | @Serializable
10 | data class SceneActionReference(
11 | /**
12 | * The resource to apply the action to.
13 | */
14 | val target: ResourceReference,
15 |
16 | /**
17 | * The action to be applied to the resource.
18 | */
19 | val action: SceneAction,
20 | )
21 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/interface.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/scenes/GetSceneCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.scenes
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object GetSceneCommand: AuthorizedShadeCommand(
8 | help = "Get information for a specific Scene",
9 | ) {
10 | private val id by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val scene = shade.scenes.getScene(id)
14 |
15 | echoScene(scene)
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/grouped-lights/src/commonMain/kotlin/inkapplications/shade/groupedlights/structures/GroupDimmingInfo.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.groupedlights.structures
2 |
3 | import inkapplications.shade.serialization.WholePercentageSerializer
4 | import inkapplications.spondee.scalar.Percentage
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Info about a light group's dimming status and capabilities.
9 | */
10 | @Serializable
11 | data class GroupDimmingInfo(
12 | /**
13 | * Current brightness value.
14 | */
15 | @Serializable(with = WholePercentageSerializer::class)
16 | val brightness: Percentage,
17 | )
18 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/ColorParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import com.github.ajalt.colormath.Color
4 | import inkapplications.shade.lights.structures.Chromaticity
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Color setting parameters for a light
10 | */
11 | @Serializable
12 | data class ColorParameters(
13 | /**
14 | * Color to set the light to
15 | */
16 | @SerialName("xy")
17 | @Serializable(with = Chromaticity.ColorSerializer::class)
18 | val color: Color? = null,
19 | )
20 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/TimedLightingEffectInfo.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Basic feature containing timed effect properties.
8 | */
9 | @Serializable
10 | data class TimedLightingEffectInfo(
11 | /**
12 | * Current lighting effect
13 | */
14 | val status: TimedLightEffect,
15 |
16 | /**
17 | * List of supported lighting effects on this device.
18 | */
19 | @SerialName("effect_values")
20 | val values: List
21 | )
22 |
--------------------------------------------------------------------------------
/events/src/commonMain/kotlin/inkapplications/shade/events/EventSerializerContainer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.events
2 |
3 | import kotlinx.serialization.DeserializationStrategy
4 |
5 | /**
6 | * Stores the serializers used for deserializing event stream data.
7 | */
8 | interface EventSerializerContainer {
9 | /**
10 | * Register a deserializer for a specific event type.
11 | *
12 | * @param type The key used to designate this type on the json object.
13 | * @param deserializer Deserializer used for events of [type]
14 | */
15 | fun setDeserializer(type: String, deserializer: DeserializationStrategy)
16 | }
17 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/GradientMode.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Mode in which gradient points are currently being deployed.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class GradientMode(val key: String) {
12 | companion object {
13 | val InterpolatedPalette = GradientMode("interpolated_palette")
14 | val InterpolatedPaletteMirrored = GradientMode("interpolated_palette_mirrored")
15 | val RandomPixelated = GradientMode("random_pixelated")
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/serialization/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | api(libs.spondee)
13 | }
14 | }
15 |
16 | val jvmTest by getting {
17 | dependencies {
18 | implementation(libs.test.core)
19 | implementation(libs.test.junit)
20 | implementation(libs.test.annotations)
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/resources/ListResourcesCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.resources
2 |
3 | import inkapplications.shade.cli.AuthorizedShadeCommand
4 |
5 | object ListResourcesCommand: AuthorizedShadeCommand(
6 | help = "Get all resources for the bridge"
7 | ) {
8 | override suspend fun runCommand(): Int {
9 | val resources = shade.resources.listResources()
10 |
11 | logger.debug("Got Resources: $resources")
12 | resources.forEach { resource ->
13 | echo("${resource.id}:")
14 | echo(" Type: ${resource.type}")
15 | }
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/rooms/RoomOutput.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.rooms
2 |
3 | import com.github.ajalt.clikt.core.CliktCommand
4 | import com.github.ajalt.clikt.output.TermUi
5 | import inkapplications.shade.rooms.structures.Room
6 |
7 | fun CliktCommand.echoRoom(room: Room) {
8 | echo("${room.id.value}:")
9 | echo(" Name: ${room.metadata.name}")
10 | echo(" Archetype: ${room.metadata.archetype}")
11 | echo(" Children:")
12 | room.children.forEach {
13 | echo(" - $it")
14 | }
15 | echo(" Services:")
16 | room.services.forEach {
17 | echo(" - $it")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/zones/ZoneOutput.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.zones
2 |
3 | import com.github.ajalt.clikt.core.CliktCommand
4 | import com.github.ajalt.clikt.output.TermUi
5 | import inkapplications.shade.zones.structures.Zone
6 |
7 | fun CliktCommand.echoZone(zone: Zone) {
8 | echo("${zone.id.value}:")
9 | echo(" Name: ${zone.metadata.name}")
10 | echo(" Archetype: ${zone.metadata.archetype}")
11 | echo(" Children:")
12 | zone.children.forEach {
13 | echo(" - $it")
14 | }
15 | echo(" Services:")
16 | zone.services.forEach {
17 | echo(" - $it")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/events/src/commonMain/kotlin/inkapplications/shade/events/EventsModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.events
2 |
3 | import inkapplications.shade.internals.InternalsModule
4 | import kimchi.logger.KimchiLogger
5 |
6 | class EventsModule(
7 | internal val internalsModule: InternalsModule,
8 | private val logger: KimchiLogger,
9 | ) {
10 | internal val eventDeserializer = CompositeEventDeserializer(internalsModule.json, logger)
11 | /**
12 | * Provides access to the container that configures SSE serialization
13 | * for the event stream.
14 | */
15 | val eventSerializerContainer: EventSerializerContainer = eventDeserializer
16 | }
17 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/rooms/DeleteRoomCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.rooms
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object DeleteRoomCommand: AuthorizedShadeCommand(
8 | help = "Delete a room from the hue bridge"
9 | ) {
10 | private val roomId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val response = shade.rooms.deleteRoom(roomId)
14 |
15 | logger.debug("Got response: $response")
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/rooms/GetRoomCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.rooms
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object GetRoomCommand: AuthorizedShadeCommand(
8 | help = "Get data for a specific room"
9 | ) {
10 | private val roomId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val room = shade.rooms.getRoom(roomId)
14 |
15 | logger.debug("Got Room: $room")
16 | echoRoom(room)
17 |
18 | return 0
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/zones/DeleteRoomCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.zones
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object DeleteZoneCommand: AuthorizedShadeCommand(
8 | help = "Delete a zone from the hue bridge"
9 | ) {
10 | private val zoneId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val response = shade.zones.deleteZone(zoneId)
14 |
15 | logger.debug("Got response: $response")
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/structures/SceneMetadata.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.structures
2 |
3 | import inkapplications.shade.structures.ResourceReference
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * User-configured metadata for the scene.
8 | */
9 | @Serializable
10 | data class SceneMetadata(
11 | /**
12 | * Human readable name of a resource
13 | */
14 | val name: String,
15 |
16 | /**
17 | * Reference with unique identifier for the image representing the scene only accepting “rtype”: “public_image” on creation
18 | */
19 | val image: ResourceReference? = null,
20 | )
21 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/field-variable.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/parameters/DeviceMetadataParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.parameters
2 |
3 | import inkapplications.shade.devices.structures.ProductArchetype
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * User-configured metadata configurable for the device.
8 | */
9 | @Serializable
10 | data class DeviceMetadataParameters(
11 | /**
12 | * Human readable name to assign to the device.
13 | */
14 | val name: String? = null,
15 |
16 | /**
17 | * User-assigned product type of device to assign to the device.
18 | */
19 | val archetype: ProductArchetype? = null,
20 | )
21 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/field-variable.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/rooms/src/commonMain/kotlin/inkapplications/shade/rooms/parameters/RoomCreateParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.rooms.parameters
2 |
3 | import inkapplications.shade.structures.ResourceReference
4 | import inkapplications.shade.structures.SegmentMetadata
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Data used for creating a new room type via the bridge.
9 | */
10 | @Serializable
11 | data class RoomCreateParameters(
12 | /**
13 | * Configuration data for the room.
14 | */
15 | val metadata: SegmentMetadata,
16 |
17 | /**
18 | * Devices to group by the Room
19 | */
20 | val children: List,
21 | )
22 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/lights/GetLightCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.lights
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object GetLightCommand: AuthorizedShadeCommand(
8 | help = "Get data for a specific light"
9 | ) {
10 | private val lightId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val light = shade.lights.getLight(lightId)
14 |
15 | logger.debug("Got Light: $light")
16 | echoLight(light)
17 |
18 | return 0
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/filter.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/events/DimmingInfoEvent.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.events
2 |
3 | import inkapplications.shade.serialization.WholePercentageSerializer
4 | import inkapplications.shade.structures.UndocumentedApi
5 | import inkapplications.spondee.scalar.Percentage
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Info about a light's dimming status and capabilities.
10 | */
11 | @Serializable
12 | @UndocumentedApi
13 | data class DimmingInfoEvent(
14 | /**
15 | * Current brightness value.
16 | */
17 | @Serializable(with = WholePercentageSerializer::class)
18 | val brightness: Percentage,
19 | )
20 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/ResourceReference.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * A reference to another object in the API
8 | */
9 | @Serializable
10 | data class ResourceReference(
11 | /**
12 | * The unique id of the referenced resource
13 | */
14 | @SerialName("rid")
15 | val id: ResourceId,
16 |
17 | /**
18 | * The type of the referenced resource
19 | */
20 | @SerialName("rtype")
21 | val type: ResourceType,
22 | ) {
23 | override fun toString(): String = "[$type:$id]"
24 | }
25 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/SegmentMetadataUpdate.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Configuration data for a segment of devices, such as a room or zone.@author
7 | *
8 | * This represents the modifyable options for [SegmentMetadata] as optional
9 | * properties.
10 | */
11 | @Serializable
12 | data class SegmentMetadataUpdate(
13 | /**
14 | * Category of type/purpose of the segment
15 | */
16 | val archetype: SegmentArchetype? = null,
17 |
18 | /**
19 | * Human readable name of the room.
20 | */
21 | val name: String? = null,
22 | )
23 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/devices/DeleteDeviceCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.devices
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object DeleteDeviceCommand: AuthorizedShadeCommand(
8 | help = "Delete a device from the hue bridge",
9 | ) {
10 | private val deviceId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val response = shade.devices.deleteDevice(deviceId)
14 |
15 | logger.debug("Got response: $response")
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/GradientValue.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Basic feature containing gradient properties.
7 | */
8 | @Serializable
9 | data class GradientValue(
10 | /**
11 | * Collection of gradients points.
12 | */
13 | val points: List,
14 |
15 | /**
16 | * Mode in which the points are currently being deployed.
17 | */
18 | val mode: GradientMode,
19 | ) {
20 | init {
21 | if (points.size > 5) throw IllegalArgumentException("Gradient cannot contain more than 5 points")
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/serialization/src/commonMain/kotlin/inkapplications/shade/serialization/FractionalPercentageSerializer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.serialization
2 |
3 | import inkapplications.spondee.scalar.Percentage
4 | import inkapplications.spondee.scalar.decimalPercentage
5 | import kotlinx.serialization.builtins.serializer
6 |
7 | /**
8 | * Serialize a percentage as a fraction of of a decimal (0.0-1.0)
9 | */
10 | object FractionalPercentageSerializer: DelegateSerializer(Double.serializer()) {
11 | override fun serialize(data: Percentage): Double = data.toDecimal().value.toDouble()
12 | override fun deserialize(data: Double): Percentage = data.decimalPercentage
13 | }
14 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/parameters/UpdateDeviceParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.parameters
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Configurable data for a device.
7 | */
8 | @Serializable
9 | data class UpdateDeviceParameters internal constructor(
10 | /**
11 | * User metadata for the device.
12 | */
13 | val metadata: DeviceMetadataParameters? = null,
14 | internal val identify: IdentifyParameters? = null,
15 | ) {
16 | constructor(
17 | metadata: DeviceMetadataParameters? = null,
18 | ): this(
19 | metadata = metadata,
20 | identify = null,
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/EffectsParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.lights.structures.Light
4 | import inkapplications.shade.lights.structures.LightEffect
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Basic feature containing effect properties.
9 | */
10 | @Serializable
11 | data class EffectsParameters(
12 | /**
13 | * Effect to set the light to.
14 | *
15 | * Note: this should be an effect supported by the light. The list of
16 | * supported effects is in the [Light.effects] property.
17 | */
18 | val effect: LightEffect? = null,
19 | )
20 |
--------------------------------------------------------------------------------
/zones/src/commonMain/kotlin/inkapplications/shade/zones/parameters/ZoneCreateParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.zones.parameters
2 |
3 | import inkapplications.shade.structures.ResourceReference
4 | import inkapplications.shade.structures.SegmentMetadata
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Parameters used for creating a new Zone on the Hue bridge
9 | */
10 | @Serializable
11 | data class ZoneCreateParameters(
12 | /**
13 | * Configuration metadata for the Zone.
14 | */
15 | val metadata: SegmentMetadata,
16 |
17 | /**
18 | * Child resources to group by the Zone.
19 | */
20 | val children: List,
21 | )
22 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/devices/GetDeviceCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.devices
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object GetDeviceCommand: AuthorizedShadeCommand(
8 | help = "Get a specific device by ID",
9 | ) {
10 | private val deviceId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val device = shade.devices.getDevice(deviceId)
14 |
15 | logger.debug("Got Device: $device")
16 | echoDevice(device)
17 |
18 | return 0
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/ColorTemperatureParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.serialization.MiredSerializer
4 | import inkapplications.spondee.measure.ColorTemperature
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Color temperature setting for a light
10 | */
11 | @Serializable
12 | data class ColorTemperatureParameters(
13 | /**
14 | * Color temperature to set the light to
15 | */
16 | @Serializable(with = MiredSerializer::class)
17 | @SerialName("mirek")
18 | val temperature: ColorTemperature? = null,
19 | )
20 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/groupedlights/GroupedLightsOutput.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.groupedlights
2 |
3 | import com.github.ajalt.clikt.core.CliktCommand
4 | import com.github.ajalt.clikt.output.TermUi
5 | import inkapplications.shade.groupedlights.structures.GroupedLight
6 |
7 | fun CliktCommand.echoGroup(group: GroupedLight) {
8 | echo("${group.id.value}:")
9 | echo(" Owner: ${group.owner}")
10 | group.powerInfo?.run {
11 | echo(" On: $on")
12 | }
13 | group.dimmingInfo?.run {
14 | echo(" Dimming: $brightness")
15 | }
16 | group.alertInfo?.run {
17 | echo(" Alerts: ${actionValues.joinToString()}")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/zones/src/commonMain/kotlin/inkapplications/shade/zones/parameters/ZoneUpdateParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.zones.parameters
2 |
3 | import inkapplications.shade.structures.ResourceReference
4 | import inkapplications.shade.structures.SegmentMetadataUpdate
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Parameters for updating a Zone's information.
9 | */
10 | @Serializable
11 | data class ZoneUpdateParameters(
12 | /**
13 | * Metadata configuration for the zone.
14 | */
15 | val metadata: SegmentMetadataUpdate? = null,
16 |
17 | /**
18 | * Child services to group by the Zone.
19 | */
20 | val children: List? = null,
21 | )
22 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/scenes/DeleteSceneCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.scenes
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object DeleteSceneCommand: AuthorizedShadeCommand(
8 | help = "Delete a scene from the hue bridge"
9 | ) {
10 | private val sceneId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val response = shade.scenes.deleteScene(sceneId)
14 |
15 | logger.debug("Got response: $response")
16 |
17 | echo(response)
18 |
19 | return 0
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/serialization/src/commonMain/kotlin/inkapplications/shade/serialization/WholePercentageSerializer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.serialization
2 |
3 | import inkapplications.spondee.scalar.Percentage
4 | import inkapplications.spondee.scalar.percent
5 | import inkapplications.spondee.scalar.toWholePercentage
6 | import kotlinx.serialization.builtins.serializer
7 |
8 | /**
9 | * Serialize a percentage value as an integer.
10 | */
11 | object WholePercentageSerializer: DelegateSerializer(Double.serializer()) {
12 | override fun serialize(data: Percentage): Double = data.toWholePercentage().value.toDouble()
13 | override fun deserialize(data: Double): Percentage = data.percent
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/devices/IdentifyDeviceCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.devices
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object IdentifyDeviceCommand: AuthorizedShadeCommand(
8 | help = "Trigger a visual identification sequence on a specified device",
9 | ) {
10 | private val deviceId by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val response = shade.devices.identifyDevice(deviceId)
14 |
15 | logger.debug("Got response: $response")
16 |
17 | return 0
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/groupedlights/GetGroupedLightCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.groupedlights
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import inkapplications.shade.cli.AuthorizedShadeCommand
5 | import inkapplications.shade.cli.resourceId
6 |
7 | object GetGroupedLightCommand: AuthorizedShadeCommand(
8 | help = "Get data for a specific grouped light"
9 | ) {
10 | private val id by argument().resourceId()
11 |
12 | override suspend fun runCommand(): Int {
13 | val group = shade.groupedLights.getGroup(id)
14 |
15 | logger.debug("Got Group: $group")
16 | echoGroup(group)
17 |
18 | return 0
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/DimmingParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.serialization.WholePercentageSerializer
4 | import inkapplications.spondee.scalar.Percentage
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Brightness changes for a light
9 | */
10 | @Serializable
11 | data class DimmingParameters(
12 | /**
13 | * Brightness percentage.
14 | *
15 | * Note: This cannot be zero. Specifying zero will use the lowest supported
16 | * brightness by the light.
17 | */
18 | @Serializable(with = WholePercentageSerializer::class)
19 | val brightness: Percentage,
20 | )
21 |
--------------------------------------------------------------------------------
/rooms/src/commonMain/kotlin/inkapplications/shade/rooms/parameters/RoomUpdateParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.rooms.parameters
2 |
3 | import inkapplications.shade.structures.ResourceReference
4 | import inkapplications.shade.structures.SegmentMetadataUpdate
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Parameters that can be specified when updating a room on the hue bridge.
9 | */
10 | @Serializable
11 | data class RoomUpdateParameters(
12 | /**
13 | * Configuration data for the room.
14 | */
15 | val metadata: SegmentMetadataUpdate? = null,
16 |
17 | /**
18 | * Devices to group by the Room
19 | */
20 | val children: List? = null,
21 | )
22 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/events/ColorInfoEvent.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.events
2 |
3 | import com.github.ajalt.colormath.Color
4 | import inkapplications.shade.lights.structures.Chromaticity
5 | import inkapplications.shade.structures.UndocumentedApi
6 | import kotlinx.serialization.SerialName
7 | import kotlinx.serialization.Serializable
8 |
9 | /**
10 | * Information about a light's color and color capabilities.
11 | */
12 | @Serializable
13 | @UndocumentedApi
14 | data class ColorInfoEvent(
15 | /**
16 | * Current color of the light
17 | */
18 | @SerialName("xy")
19 | @Serializable(with = Chromaticity.ColorSerializer::class)
20 | val color: Color,
21 | )
22 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/structures/SceneRecallStatus.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * When writing active, the actions in the scene are executed on the target.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class SceneRecallStatus(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val Active = SceneRecallStatus("active")
16 |
17 | /**
18 | * Starts dynamic scene with colors in the Palette object.
19 | */
20 | val DynamicPalette = SceneRecallStatus("dynamic_palette")
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/AuthorizedShadeCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli
2 |
3 | import com.github.ajalt.clikt.parameters.options.option
4 | import inkapplications.shade.structures.AuthToken
5 | import kotlinx.coroutines.flow.MutableStateFlow
6 | import kotlinx.coroutines.flow.StateFlow
7 |
8 | abstract class AuthorizedShadeCommand(
9 | help: String,
10 | ): ShadeCommand(help) {
11 | private val key by option(
12 | help = "Application API Key/Token for connecting to the hue bridge"
13 | )
14 |
15 | override val authToken: StateFlow by lazy {
16 | key?.let { AuthToken(applicationKey = it) }?.let(::MutableStateFlow)
17 | ?: fileProperties.authToken
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/internals/src/commonMain/kotlin/inkapplications/shade/internals/SseClient.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import kotlinx.serialization.DeserializationStrategy
5 |
6 | /**
7 | * Internal client used for listening to event streams from the Hue bridge.
8 | */
9 | interface SseClient {
10 | /**
11 | * Open a server-side-event stream.
12 | *
13 | * @param pathSegments The URL path to the event stream to listen to.
14 | * @param dataDeserializer deserializer to use for all events emitted.
15 | */
16 | fun openSse(
17 | pathSegments: Array,
18 | dataDeserializer: DeserializationStrategy,
19 | ): Flow
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/logo-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
15 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/interface-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/events/src/jvmMain/kotlin/shade/events/ShadeEvents.kt:
--------------------------------------------------------------------------------
1 | package shade.events
2 |
3 | import inkapplications.shade.events.Events
4 | import inkapplications.shade.internals.SseClient
5 | import inkapplications.shade.structures.UndocumentedApi
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.serialization.DeserializationStrategy
8 |
9 | @UndocumentedApi
10 | internal class ShadeEvents(
11 | private val sseClient: SseClient,
12 | private val eventDeserializer: DeserializationStrategy>,
13 | ): Events {
14 | override fun bridgeEvents(): Flow> {
15 | return sseClient.openSse(
16 | pathSegments = arrayOf("eventstream", "clip", "v2"),
17 | dataDeserializer = eventDeserializer,
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/interface-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/devices/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 |
16 | api(libs.coroutines.core)
17 | }
18 | }
19 |
20 | val commonTest by getting {
21 | dependencies {
22 | implementation(libs.test.core)
23 | implementation(libs.test.annotations)
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/events/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 |
16 | api(libs.coroutines.core)
17 | }
18 | }
19 |
20 | val commonTest by getting {
21 | dependencies {
22 | implementation(libs.test.core)
23 | implementation(libs.test.annotations)
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/rooms/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 |
16 | api(libs.coroutines.core)
17 | }
18 | }
19 |
20 | val commonTest by getting {
21 | dependencies {
22 | implementation(libs.test.core)
23 | implementation(libs.test.annotations)
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/zones/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 |
16 | api(libs.coroutines.core)
17 | }
18 | }
19 |
20 | val commonTest by getting {
21 | dependencies {
22 | implementation(libs.test.core)
23 | implementation(libs.test.annotations)
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/ShadeLightsModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights
2 |
3 | import inkapplications.shade.events.EventsModule
4 | import inkapplications.shade.internals.InternalsModule
5 | import inkapplications.shade.lights.events.LightEvent
6 | import inkapplications.shade.structures.UndocumentedApi
7 |
8 | /**
9 | * Provides Access to Light control services.
10 | */
11 | @OptIn(UndocumentedApi::class)
12 | class ShadeLightsModule(
13 | internalsModule: InternalsModule,
14 | eventsModule: EventsModule,
15 | ) {
16 | val lights: LightControls = ShadeLights(internalsModule.hueHttpClient)
17 |
18 | init {
19 | eventsModule.eventSerializerContainer.setDeserializer("light", LightEvent.serializer())
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/ColorTemperatureValue.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import inkapplications.shade.serialization.MiredSerializer
4 | import inkapplications.spondee.measure.ColorTemperature
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Describes the current color temperature of a light.
10 | */
11 | @Serializable
12 | data class ColorTemperatureValue(
13 | /**
14 | * Current color temperature of the bulb,
15 | *
16 | * This value can be null if the color is not in the ct spectrum
17 | */
18 | @SerialName("mirek")
19 | @Serializable(with = MiredSerializer::class)
20 | val temperature: ColorTemperature?,
21 | )
22 |
--------------------------------------------------------------------------------
/resources/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 |
16 | api(libs.coroutines.core)
17 | }
18 | }
19 |
20 | val commonTest by getting {
21 | dependencies {
22 | implementation(libs.test.core)
23 | implementation(libs.test.annotations)
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/Gradient.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Basic feature containing gradient properties.
8 | */
9 | @Serializable
10 | data class Gradient(
11 | /**
12 | * Collection of gradients points.
13 | */
14 | val points: List,
15 |
16 | /**
17 | * Number of color points that gradient lamp is capable of showing with gradience.
18 | */
19 | @SerialName("points_capable")
20 | val pointsCapable: Int,
21 | ) {
22 | init {
23 | if (points.size > 5) throw IllegalArgumentException("Gradient cannot contain more than 5 points")
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/structures/SceneRecallAction.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * When writing active, the actions in the scene are executed on the target.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class SceneRecallAction(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val Active = SceneRecallAction("active")
16 |
17 | /**
18 | * Starts dynamic scene with colors in the Palette object.
19 | */
20 | val DynamicPalette = SceneRecallAction("dynamic_palette")
21 | val Static = SceneRecallAction("static")
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/anchor-copy-button.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/enum-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/enum-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/typealias-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/typealias-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/GradientParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.lights.structures.GradientPoint
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Basic feature containing gradient properties.
8 | */
9 | @Serializable
10 | data class GradientParameters(
11 | /**
12 | * Collection of gradients points.
13 | *
14 | * Note: minimum of 2 points need to be provided.
15 | */
16 | val points: List,
17 | ) {
18 | init {
19 | if (points.size < 2) throw IllegalArgumentException("Gradient must contain at least two points")
20 | if (points.size > 5) throw IllegalArgumentException("Gradient cannot contain more than 5 points")
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | id("ink.publishing")
4 | }
5 |
6 | kotlin {
7 | sourceSets {
8 | val commonMain by getting {
9 | dependencies {
10 | implementation(projects.internals)
11 | api(projects.auth)
12 | api(projects.discover)
13 | api(projects.devices)
14 | api(projects.events)
15 | api(projects.groupedLights)
16 | api(projects.lights)
17 | api(projects.resources)
18 | api(projects.rooms)
19 | api(projects.scenes)
20 | api(projects.structures)
21 | api(projects.zones)
22 |
23 | api(libs.kimchi.logger)
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lights/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 | api(projects.events)
16 |
17 | api(libs.coroutines.core)
18 | }
19 | }
20 |
21 | val commonTest by getting {
22 | dependencies {
23 | implementation(libs.test.core)
24 | implementation(libs.test.annotations)
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/resources/src/commonMain/kotlin/inkapplications/shade/resources/structures/Resource.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.resources.structures
2 |
3 | import inkapplications.shade.structures.ResourceId
4 | import inkapplications.shade.structures.ResourceType
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Hue API resource.
10 | */
11 | @Serializable
12 | data class Resource(
13 | /**
14 | * Unique ID for the resource.
15 | */
16 | val id: ResourceId,
17 |
18 | /**
19 | * Resource object type.
20 | */
21 | val type: ResourceType? = null,
22 |
23 | @Deprecated("V1 Resource. Left for migration purposes only, may be removed at any point by API or SDK.")
24 | @SerialName("id_v1")
25 | val v1Id: String? = null,
26 | )
27 |
--------------------------------------------------------------------------------
/scenes/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 | api(projects.lights)
16 |
17 | api(libs.coroutines.core)
18 | }
19 | }
20 |
21 | val commonTest by getting {
22 | dependencies {
23 | implementation(libs.test.core)
24 | implementation(libs.test.annotations)
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/structures/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.serialization)
13 | api(libs.coroutines.core)
14 | api(libs.datetime)
15 | api(libs.spondee)
16 | api("com.github.ajalt.colormath:colormath:3.6.0")
17 | }
18 | }
19 |
20 | val commonTest by getting {
21 | dependencies {
22 | implementation(libs.test.core)
23 | implementation(libs.test.annotations)
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/grouped-lights/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 | api(projects.lights)
16 |
17 | api(libs.coroutines.core)
18 | }
19 | }
20 |
21 | val commonTest by getting {
22 | dependencies {
23 | implementation(libs.test.core)
24 | implementation(libs.test.annotations)
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internals/src/commonMain/kotlin/inkapplications/shade/internals/BaseUrl.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | /**
4 | * Builds the base path of API requests
5 | */
6 | internal object BaseUrl {
7 | private val API_V2 = arrayOf("clip", "v2")
8 | private val API_V1 = arrayOf("api")
9 |
10 | /**
11 | * Build a V1 API Request's base-url
12 | *
13 | * @param pathSegments The path segments in the request to add after the base-url
14 | */
15 | fun v1(vararg pathSegments: String): Array = API_V1 + pathSegments
16 |
17 | /**
18 | * Build a V2 API Request's base-url
19 | *
20 | * @param pathSegments The path segments in the request to add after the base-url
21 | */
22 | fun v2(vararg pathSegments: String): Array = API_V2 + pathSegments
23 | }
24 |
--------------------------------------------------------------------------------
/internals/src/commonMain/kotlin/inkapplications/shade/internals/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.structures.HueConfigurationContainer
4 | import inkapplications.shade.structures.SecurityStrategy
5 | import io.ktor.client.engine.*
6 | import kimchi.logger.EmptyLogger
7 | import kimchi.logger.KimchiLogger
8 | import kotlinx.serialization.json.Json
9 |
10 | /**
11 | * Provides platform-specific dependencies for the SDK.
12 | */
13 | expect class PlatformModule(
14 | configurationContainer: HueConfigurationContainer,
15 | json: Json,
16 | logger: KimchiLogger = EmptyLogger,
17 | ) {
18 | /**
19 | * Create Ktor http engine based on the platform.
20 | */
21 | fun createEngine(securityStrategy: SecurityStrategy): HttpClientEngineFactory<*>
22 | }
23 |
--------------------------------------------------------------------------------
/discover/src/commonMain/kotlin/inkapplications/shade/discover/structures/Bridge.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover.structures
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * A discovered Hue Bridge.
8 | */
9 | @Serializable
10 | data class Bridge(
11 | /**
12 | * The unique ID of this hue bridge.
13 | */
14 | val id: BridgeId,
15 |
16 | /**
17 | * The local network IP address of the bridge.
18 | */
19 | @SerialName("internalipaddress")
20 | val localIp: String,
21 |
22 | /**
23 | * The port to be used for the Hue API.
24 | *
25 | * Note: This field was not always available on older bridges, and will
26 | * default to an insecure 80 HTTP port if not specified.
27 | */
28 | val port: Int = 80,
29 | )
30 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/LightSignalStatus.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.datetime.Instant
4 | import kotlinx.datetime.serializers.InstantIso8601Serializer
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Status of active light signal.
10 | */
11 | @Serializable
12 | data class LightSignalStatus(
13 | /**
14 | * Indicates which signal is currently active.
15 | */
16 | val signal: LightSignal,
17 | /**
18 | * Timestamp indicating when the active signal is expected to end.
19 | * Value is not set if there is no_signal
20 | */
21 | @SerialName("estimated_end")
22 | @Serializable(with = InstantIso8601Serializer::class)
23 | val estimatedEnd: Instant? = null,
24 | )
25 |
--------------------------------------------------------------------------------
/events/api/events.api:
--------------------------------------------------------------------------------
1 | public abstract interface class inkapplications/shade/events/EventSerializerContainer {
2 | public abstract fun setDeserializer (Ljava/lang/String;Lkotlinx/serialization/DeserializationStrategy;)V
3 | }
4 |
5 | public abstract interface class inkapplications/shade/events/Events {
6 | public abstract fun bridgeEvents ()Lkotlinx/coroutines/flow/Flow;
7 | }
8 |
9 | public final class inkapplications/shade/events/EventsModule {
10 | public fun (Linkapplications/shade/internals/InternalsModule;Lkimchi/logger/KimchiLogger;)V
11 | public final fun getEventSerializerContainer ()Linkapplications/shade/events/EventSerializerContainer;
12 | }
13 |
14 | public final class shade/events/EventsModuleKt {
15 | public static final fun getEvents (Linkapplications/shade/events/EventsModule;)Linkapplications/shade/events/Events;
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/structures/src/jvmTest/kotlin/inkapplications/shade/structures/VersionStringTest.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import junit.framework.TestCase.assertTrue
4 | import org.junit.Test
5 | import kotlin.test.assertEquals
6 | import kotlin.test.assertFalse
7 |
8 | class VersionStringTest {
9 | @Test
10 | fun testVersionParsing() {
11 | val version = VersionString("1.2.3")
12 |
13 | assertEquals("1", version.major?.value)
14 | assertEquals("2", version.minor?.value)
15 | assertEquals("3", version.patch?.value)
16 | }
17 |
18 | @Test
19 | fun testInvalidVersion() {
20 | val result = runCatching {
21 | VersionString("asdf")
22 | }
23 |
24 | assertFalse(result.isSuccess)
25 | assertTrue(result.exceptionOrNull() is IllegalArgumentException)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/auth/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | implementation(projects.internals)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 | api(libs.datetime)
16 |
17 | api(libs.coroutines.core)
18 | }
19 | }
20 |
21 | val commonTest by getting {
22 | dependencies {
23 | implementation(libs.test.core)
24 | implementation(libs.test.annotations)
25 | implementation(libs.coroutines.test)
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/VersionString.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Software major/minor/patch version string.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class VersionString(val full: String) {
12 | private val pattern get() = Regex("^(\\d+)\\.(\\d+)\\.(\\d+)$")
13 | private val groups get() = pattern.matchEntire(full)?.groups
14 | val major get() = groups?.get(1)
15 | val minor get() = groups?.get(2)
16 | val patch get() = groups?.get(3)
17 |
18 | init {
19 | if (!pattern.matches(full)) {
20 | throw IllegalArgumentException("Version string does not match expected pattern. Got: $full")
21 | }
22 | }
23 |
24 | override fun toString(): String = full
25 | }
26 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/ColorTemperatureDeltaParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.serialization.ExplicitMiredSerializer
4 | import inkapplications.spondee.measure.Mireds
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Relative color temperature change for a light
10 | */
11 | @Serializable
12 | data class ColorTemperatureDeltaParameters(
13 | /**
14 | * The type of delta defined
15 | */
16 | val action: DeltaAction,
17 |
18 | /**
19 | * Delta of color temperature to be added or removed from the light's
20 | * current state.
21 | */
22 | @SerialName("mirek_delta")
23 | @Serializable(with = ExplicitMiredSerializer::class)
24 | val temperatureDelta: Mireds? = null,
25 | )
26 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/theme-toggle.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/auth/src/jvmTest/kotlin/inkapplications/shade/auth/structures/AppIdSerializerTest.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.auth.structures
2 |
3 | import kotlinx.serialization.decodeFromString
4 | import kotlinx.serialization.encodeToString
5 | import kotlinx.serialization.json.Json
6 | import kotlin.test.Test
7 | import kotlin.test.assertEquals
8 |
9 | class AppIdSerializerTest {
10 | @Test
11 | fun serializer() {
12 | val appId = AppId(
13 | appName = "test-name",
14 | instanceName = "test-instance",
15 | )
16 | val json = """"test-name#test-instance"""".trimIndent()
17 |
18 | val encodedResult = Json.encodeToString(appId)
19 | val decodedResult = Json.decodeFromString(json)
20 |
21 | assertEquals(json, encodedResult, "Encoded result")
22 | assertEquals(appId, decodedResult, "Decoding result")
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/DimmingInfo.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import inkapplications.shade.serialization.WholePercentageSerializer
4 | import inkapplications.spondee.scalar.Percentage
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Info about a light's dimming status and capabilities.
10 | */
11 | @Serializable
12 | data class DimmingInfo(
13 | /**
14 | * Current brightness value.
15 | */
16 | @Serializable(with = WholePercentageSerializer::class)
17 | val brightness: Percentage,
18 |
19 | /**
20 | * Percentage of the maximum lumen the device outputs on minimum brightness.
21 | */
22 | @Serializable(with = WholePercentageSerializer::class)
23 | @SerialName("min_dim_level")
24 | val minimum: Percentage? = null,
25 | )
26 |
--------------------------------------------------------------------------------
/auth/src/commonMain/kotlin/inkapplications/shade/auth/structures/AppId.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.auth.structures
2 |
3 | import inkapplications.shade.serialization.DelegateSerializer
4 | import kotlinx.serialization.Serializable
5 | import kotlinx.serialization.builtins.serializer
6 |
7 | /**
8 | * Identifies an app connection upon auth
9 | */
10 | @Serializable(with = AppId.Serializer::class)
11 | data class AppId(
12 | val appName: String,
13 | val instanceName: String,
14 | ) {
15 | internal object Serializer: DelegateSerializer(String.serializer()) {
16 | override fun serialize(data: AppId): String = "${data.appName}#${data.instanceName}"
17 | override fun deserialize(data: String): AppId = data.split('#').let {
18 | AppId(
19 | appName = it[0],
20 | instanceName = it[1],
21 | )
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/grouped-lights/src/commonMain/kotlin/inkapplications/shade/groupedlights/ShadeGroupedLightsModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.groupedlights
2 |
3 | import inkapplications.shade.events.EventsModule
4 | import inkapplications.shade.groupedlights.events.GroupedLightEvent
5 | import inkapplications.shade.internals.InternalsModule
6 | import inkapplications.shade.lights.events.LightEvent
7 | import inkapplications.shade.structures.UndocumentedApi
8 |
9 | /**
10 | * Provides Access to grouped light services.
11 | */
12 | @OptIn(UndocumentedApi::class)
13 | class ShadeGroupedLightsModule(
14 | internalsModule: InternalsModule,
15 | eventsModule: EventsModule,
16 | ) {
17 | val groupedLights: GroupedLightControls = ShadeGroupedLights(internalsModule.hueHttpClient)
18 |
19 | init {
20 | eventsModule.eventSerializerContainer.setDeserializer("grouped_light", GroupedLightEvent.serializer())
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/ColorInfo.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import com.github.ajalt.colormath.Color
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Information about a light's color and color capabilities.
9 | */
10 | @Serializable
11 | data class ColorInfo(
12 | /**
13 | * Current color of the light
14 | */
15 | @SerialName("xy")
16 | @Serializable(with = Chromaticity.ColorSerializer::class)
17 | val color: Color,
18 |
19 | /**
20 | * Simple Hue gamut type
21 | */
22 | @SerialName("gamut_type")
23 | val gamutType: GamutType,
24 |
25 | /**
26 | * Color gamut of color bulb. Some bulbs do not properly return the Gamut information. In this case this is not present.
27 | */
28 | @Serializable
29 | val gamut: Gamut? = null,
30 | )
31 |
--------------------------------------------------------------------------------
/internals/src/jsMain/kotlin/inkapplications/shade/internals/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.structures.HueConfigurationContainer
4 | import inkapplications.shade.structures.SecurityStrategy
5 | import io.ktor.client.engine.*
6 | import io.ktor.client.engine.js.*
7 | import kimchi.logger.KimchiLogger
8 | import kotlinx.serialization.json.Json
9 |
10 | actual class PlatformModule actual constructor(
11 | configurationContainer: HueConfigurationContainer,
12 | json: Json,
13 | logger: KimchiLogger
14 | ) {
15 | actual fun createEngine(securityStrategy: SecurityStrategy): HttpClientEngineFactory<*> {
16 | if (securityStrategy !is SecurityStrategy.PlatformTrust) {
17 | throw IllegalArgumentException("Javascript client cannot change security settings and must rely on the platform's trust.")
18 | }
19 |
20 | return Js
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/TimedEffectsParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.lights.structures.Light
4 | import inkapplications.shade.lights.structures.TimedLightEffect
5 | import inkapplications.shade.serialization.MillisecondDurationSerializer
6 | import kotlinx.serialization.Serializable
7 | import kotlin.time.Duration
8 |
9 | /**
10 | * Basic feature containing timed effect properties.
11 | */
12 | @Serializable
13 | data class TimedEffectsParameters(
14 | /**
15 | * Effect to set the light to.
16 | *
17 | * Note: this should be an effect supported by the light. The list of
18 | * supported effects is in the [Light.timedEffects] property.
19 | */
20 | val effect: TimedLightEffect? = null,
21 | @Serializable(with = MillisecondDurationSerializer::class)
22 | val duration: Duration? = null,
23 | )
24 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/class.svg:
--------------------------------------------------------------------------------
1 |
4 |
10 |
--------------------------------------------------------------------------------
/internals/src/iosMain/kotlin/inkapplications/shade/internals/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.structures.HueConfigurationContainer
4 | import inkapplications.shade.structures.SecurityStrategy
5 | import io.ktor.client.engine.*
6 | import io.ktor.client.engine.darwin.*
7 | import kimchi.logger.KimchiLogger
8 | import kotlinx.serialization.json.Json
9 |
10 | actual class PlatformModule actual constructor(
11 | configurationContainer: HueConfigurationContainer,
12 | json: Json,
13 | logger: KimchiLogger
14 | ) {
15 | actual fun createEngine(securityStrategy: SecurityStrategy): HttpClientEngineFactory<*> {
16 | if (securityStrategy !is SecurityStrategy.PlatformTrust) {
17 | throw IllegalArgumentException("iOS client cannot change security settings and must rely on the platform's trust.")
18 | }
19 |
20 | return Darwin
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/internals/src/nativeMain/kotlin/inkapplications/shade/internals/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.structures.HueConfigurationContainer
4 | import inkapplications.shade.structures.SecurityStrategy
5 | import io.ktor.client.engine.*
6 | import io.ktor.client.engine.cio.*
7 | import kimchi.logger.KimchiLogger
8 | import kotlinx.serialization.json.Json
9 |
10 | actual class PlatformModule actual constructor(
11 | configurationContainer: HueConfigurationContainer,
12 | json: Json,
13 | logger: KimchiLogger
14 | ) {
15 | actual fun createEngine(securityStrategy: SecurityStrategy): HttpClientEngineFactory<*> {
16 | if (securityStrategy !is SecurityStrategy.PlatformTrust) {
17 | throw IllegalArgumentException("Native client cannot change security settings and must rely on the platform's trust.")
18 | }
19 |
20 | return CIO
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/internals/src/windowsMain/kotlin/inkapplications/shade/internals/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.structures.HueConfigurationContainer
4 | import inkapplications.shade.structures.SecurityStrategy
5 | import io.ktor.client.engine.*
6 | import io.ktor.client.engine.winhttp.*
7 | import kimchi.logger.KimchiLogger
8 | import kotlinx.serialization.json.Json
9 |
10 | actual class PlatformModule actual constructor(
11 | configurationContainer: HueConfigurationContainer,
12 | json: Json,
13 | logger: KimchiLogger
14 | ) {
15 | actual fun createEngine(securityStrategy: SecurityStrategy): HttpClientEngineFactory<*> {
16 | if (securityStrategy !is SecurityStrategy.PlatformTrust) {
17 | throw IllegalArgumentException("Windows client cannot change security settings and must rely on the platform's trust.")
18 | }
19 |
20 | return WinHttp
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/class.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/AlertEffectType.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Wraps alert effects for lighting
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class AlertEffectType(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val Breathe = AlertEffectType("breathe")
16 |
17 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
18 | fun values(): Array = arrayOf(Breathe)
19 |
20 | @Deprecated(
21 | message = "Deprecated in favor of constructor",
22 | replaceWith = ReplaceWith("AlertEffectType(key)"),
23 | )
24 | fun valueOf(key: String) = values().single { it.key == key }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/UndocumentedApi.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | /**
4 | * Annotation used to designate APIs in the hue system that were not documented.
5 | *
6 | * Since these methods don't have documentation in Hue's official docs, the
7 | * data types and methods may change at any time, including on the Bridge
8 | * itself, meaning the code may break over time. Data structures are filled in
9 | * by interpreting responses and are likely incomplete and may be incorrect
10 | * or incomplete for different devices.
11 | * Changes made to methods with this annotation will NOT be considered a
12 | * breaking change between versions of the Shade SDK.
13 | *
14 | * Use with caution.
15 | */
16 | @RequiresOptIn(
17 | message = "This uses an undocumented Hue API and may be unreliable, incorrect, and is subject to change at any time."
18 | )
19 | @Retention(AnnotationRetention.BINARY)
20 | annotation class UndocumentedApi
21 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/theme-toggle.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/serialization/src/commonMain/kotlin/inkapplications/shade/serialization/ExplicitMiredSerializer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.serialization
2 |
3 | import inkapplications.spondee.measure.Mireds
4 | import inkapplications.spondee.measure.mireds
5 | import inkapplications.spondee.structure.convert
6 | import kotlinx.serialization.builtins.serializer
7 | import kotlin.math.roundToInt
8 |
9 | /**
10 | * Convert a mired color temperature to/from an int value
11 | *
12 | * This is used for color temperature delta values that require the unit in
13 | * mireds. Since mireds and kelvin do not have a linear relationship, a delta
14 | * value cannot be translated between the two units. So this converter
15 | * takes and produces an explicit mired unit only.
16 | */
17 | object ExplicitMiredSerializer: DelegateSerializer(Int.serializer()) {
18 | override fun serialize(data: Mireds): Int = data.convert { roundToInt() }
19 | override fun deserialize(data: Int): Mireds = data.mireds
20 | }
21 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/LightMode.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Wrap's a light mode flag/values
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class LightMode(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val Normal = LightMode("normal")
16 | val Streaming = LightMode("streaming")
17 |
18 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
19 | fun values(): Array = arrayOf(Normal, Streaming)
20 |
21 | @Deprecated(
22 | message = "Deprecated in favor of constructor",
23 | replaceWith = ReplaceWith("LightMode(key)"),
24 | )
25 | fun valueOf(key: String) = values().single { it.key == key }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/object.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/object.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/events/ColorTemperatureInfoEvent.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.events
2 |
3 | import inkapplications.shade.serialization.MiredSerializer
4 | import inkapplications.shade.structures.UndocumentedApi
5 | import inkapplications.spondee.measure.ColorTemperature
6 | import kotlinx.serialization.SerialName
7 | import kotlinx.serialization.Serializable
8 |
9 | /**
10 | * Information about the bulb's color temperature and capabilities.
11 | */
12 | @Serializable
13 | @UndocumentedApi
14 | data class ColorTemperatureInfoEvent(
15 | /**
16 | * Current color temperature of the bulb,
17 | *
18 | * This value can be null if the color is not in the ct spectrum
19 | */
20 | @SerialName("mirek")
21 | @Serializable(with = MiredSerializer::class)
22 | val temperature: ColorTemperature?,
23 |
24 | /**
25 | * Indication whether the value is valid for this hardware.
26 | */
27 | @SerialName("mirek_valid")
28 | val valid: Boolean,
29 | )
30 |
--------------------------------------------------------------------------------
/discover/src/commonMain/kotlin/inkapplications/shade/discover/DiscoverModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import io.ktor.client.*
4 | import io.ktor.client.plugins.contentnegotiation.*
5 | import io.ktor.serialization.kotlinx.json.*
6 | import kotlinx.serialization.json.Json
7 |
8 | /**
9 | * Provides access to Hue's discovery services.
10 | */
11 | class DiscoverModule {
12 | private val platformModule = PlatformModule()
13 |
14 | private val json = Json {
15 | ignoreUnknownKeys = true
16 | }
17 |
18 | private val client = HttpClient(platformModule.createEngine()) {
19 | install(ContentNegotiation) {
20 | json(json)
21 | }
22 | }
23 |
24 | /**
25 | * Discovery implementation using Hue's online discovery protocol.
26 | *
27 | * Note that this implementation requires an active internet connection
28 | * on both the client and bridge device to function.
29 | */
30 | val onlineDiscovery: BridgeDiscovery = KtorDiscovery(client)
31 | }
32 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/DimmingDeltaParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.serialization.WholePercentageSerializer
4 | import inkapplications.spondee.scalar.Percentage
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Relative brightness changes for a light.
10 | */
11 | @Serializable
12 | data class DimmingDeltaParameters(
13 | /**
14 | * Type of delta being defined
15 | */
16 | val action: DeltaAction,
17 |
18 | /**
19 | * Percentage brightness to be added to the light.
20 | *
21 | * Note that this percentage is relative addition, not multiplicative.
22 | * e.g. Specifying 10% here for light at 50% brightness will result in
23 | * the light being 60% brightness.
24 | */
25 | @Serializable(with = WholePercentageSerializer::class)
26 | @SerialName("brightness_delta")
27 | val brightnessDelta: Percentage? = null,
28 | )
29 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/Gamut.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import com.github.ajalt.colormath.Illuminant
4 | import com.github.ajalt.colormath.model.RGBColorSpace
5 | import com.github.ajalt.colormath.model.xyY
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * RGB Chromaticity values defining a color space gamut
10 | */
11 | @Serializable
12 | data class Gamut(
13 | @Serializable(with = Chromaticity.CieChromaticitySerializer::class)
14 | val red: xyY,
15 | @Serializable(with = Chromaticity.CieChromaticitySerializer::class)
16 | val green: xyY,
17 | @Serializable(with = Chromaticity.CieChromaticitySerializer::class)
18 | val blue: xyY,
19 | ) {
20 | fun toColorSpace() = RGBColorSpace(
21 | name = "Hue Specified Colorspace",
22 | whitePoint = Illuminant.D65,
23 | transferFunctions = RGBColorSpace.LinearTransferFunctions,
24 | r = red,
25 | g = green,
26 | b = blue,
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/LightSignal.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Indicates which signal is currently active for a light signal.
8 | */
9 | @Serializable
10 | @JvmInline
11 | value class LightSignal(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val NoSignal = LightSignal("no_signal")
16 | val OnOff = LightSignal("on_off")
17 |
18 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
19 | fun values(): Array = arrayOf(NoSignal, OnOff)
20 |
21 | @Deprecated(
22 | message = "Deprecated in favor of constructor",
23 | replaceWith = ReplaceWith("LightSignal(key)"),
24 | )
25 | fun valueOf(key: String) = values().single { it.key == key }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/TimedLightEffect.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Timed Effect applied to a light
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class TimedLightEffect(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val Sunrise = TimedLightEffect("sunrise")
16 | val None = TimedLightEffect("no_effect")
17 |
18 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
19 | fun values(): Array = arrayOf(Sunrise, None)
20 |
21 | @Deprecated(
22 | message = "Deprecated in favor of constructor",
23 | replaceWith = ReplaceWith("TimedLightEffect(key)"),
24 | )
25 | fun valueOf(key: String) = values().single { it.key == key }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/serialization/src/commonMain/kotlin/inkapplications/shade/serialization/MiredSerializer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.serialization
2 |
3 | import inkapplications.spondee.measure.ColorTemperature
4 | import inkapplications.spondee.measure.mireds
5 | import inkapplications.spondee.measure.toMireds
6 | import inkapplications.spondee.structure.convert
7 | import kotlinx.serialization.builtins.serializer
8 | import kotlin.math.roundToInt
9 |
10 | /**
11 | * Convert standard ColorTemperatures to/from Mireds
12 | *
13 | * Hue's API calls this a 'mirek' in an effort to make the most obscure
14 | * term for this unit, but it seems to be identical to a Mired.
15 | * Now you don't have to deal with it.
16 | */
17 | object MiredSerializer: DelegateSerializer(Int.serializer()) {
18 | override fun serialize(data: ColorTemperature): Int {
19 | return data.toMireds().convert { roundToInt() }
20 | }
21 |
22 | override fun deserialize(data: Int): ColorTemperature {
23 | return data.mireds
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019-2023 Ink Applications
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/DynamicsStatus.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Wraps potential values for [LightDynamics] state.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class DynamicsStatus(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val DynamicPalette = DynamicsStatus("dynamic_palette")
16 | val None = DynamicsStatus("none")
17 |
18 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
19 | fun values(): Array = arrayOf(None, DynamicPalette)
20 |
21 | @Deprecated(
22 | message = "Deprecated in favor of constructor",
23 | replaceWith = ReplaceWith("DynamicsStatus(key)"),
24 | )
25 | fun valueOf(key: String) = values().single { it.key == key }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/structures/ScenePalette.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.structures
2 |
3 | import inkapplications.shade.lights.structures.ColorPalette
4 | import inkapplications.shade.lights.structures.ColorTemperaturePalette
5 | import inkapplications.shade.lights.structures.DimmingValue
6 | import kotlinx.serialization.SerialName
7 | import kotlinx.serialization.Serializable
8 |
9 | /**
10 | * Group of colors that describe the palette of colors to be used when playing dynamics
11 | */
12 | @Serializable
13 | data class ScenePalette(
14 | /**
15 | * List of colors to be used when playing dynamics.
16 | */
17 | val color: List? = null,
18 |
19 | /**
20 | * List of colors to be used when playing dynamics.
21 | */
22 | val dimming: List? = null,
23 |
24 | /**
25 | * List of colors to be used when playing dynamics.
26 | */
27 | @SerialName("color_temperature")
28 | val colorTemperature: List? = null,
29 | )
30 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/class-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/class-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/DeltaAction.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Defines the type of delta argument provided in a parameter.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class DeltaAction(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val Up = DeltaAction("up")
16 | val Down = DeltaAction("down")
17 | val Stop = DeltaAction("stop")
18 |
19 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
20 | fun values(): Array = arrayOf(Up, Down, Stop)
21 |
22 | @Deprecated(
23 | message = "Deprecated in favor of constructor",
24 | replaceWith = ReplaceWith("DeltaAction(key)"),
25 | )
26 | fun valueOf(key: String) = values().single { it.key == key }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/LightEffect.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Static effect applied to a light.
8 | */
9 | @JvmInline
10 | @Serializable
11 | value class LightEffect(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | val Fire = LightEffect("fire")
16 | val Candle = LightEffect("candle")
17 | val None = LightEffect("no_effect")
18 |
19 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
20 | fun values(): Array = arrayOf(Fire, Candle, None)
21 |
22 | @Deprecated(
23 | message = "Deprecated in favor of constructor",
24 | replaceWith = ReplaceWith("LightEffect(key)"),
25 | )
26 | fun valueOf(key: String) = values().single { it.key == key }
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/serialization/src/commonMain/kotlin/inkapplications/shade/serialization/DelegateSerializer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.serialization
2 |
3 | import kotlinx.serialization.KSerializer
4 | import kotlinx.serialization.descriptors.SerialDescriptor
5 | import kotlinx.serialization.encoding.Decoder
6 | import kotlinx.serialization.encoding.Encoder
7 |
8 | /**
9 | * Delegates kotlin serialization to a backing type for simpler encoding.
10 | */
11 | abstract class DelegateSerializer(
12 | private val delegate: KSerializer,
13 | ): KSerializer {
14 | final override val descriptor: SerialDescriptor = delegate.descriptor
15 | final override fun deserialize(decoder: Decoder): DESERIALIZED = delegate.deserialize(decoder).let(::deserialize)
16 | final override fun serialize(encoder: Encoder, value: DESERIALIZED) = delegate.serialize(encoder, serialize(value))
17 |
18 | protected abstract fun serialize(data: DESERIALIZED): SERIALIZED
19 | protected abstract fun deserialize(data: SERIALIZED): DESERIALIZED
20 | }
21 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/function.svg:
--------------------------------------------------------------------------------
1 |
4 |
10 |
--------------------------------------------------------------------------------
/internals/src/commonMain/kotlin/inkapplications/shade/internals/HueStubClient.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.serialization.HueResponse
4 | import inkapplications.shade.serialization.V1HueResponse
5 | import kotlinx.serialization.KSerializer
6 |
7 | /**
8 | * A stubbed out client used for testing delegation
9 | */
10 | object HueStubClient: HueHttpClient {
11 | override suspend fun sendRequest(
12 | method: String,
13 | pathSegments: Array,
14 | responseSerializer: KSerializer>,
15 | body: REQUEST?,
16 | requestSerializer: KSerializer?
17 | ): RESPONSE = TODO("Not yet implemented")
18 |
19 | override suspend fun sendV1Request(
20 | method: String,
21 | pathSegments: Array,
22 | responseSerializer: KSerializer>>,
23 | body: REQUEST?,
24 | requestSerializer: KSerializer?
25 | ): RESPONSE = TODO("Not yet implemented")
26 | }
27 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/function.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/reference/latest/styles/jetbrains-mono.css:
--------------------------------------------------------------------------------
1 | @font-face{
2 | font-family: 'JetBrains Mono';
3 | src: url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/web/JetBrainsMono-Regular.eot') format('embedded-opentype'),
4 | url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/webfonts/JetBrainsMono-Regular.woff2') format('woff2'),
5 | url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/ttf/JetBrainsMono-Regular.ttf') format('truetype');
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 |
10 | @font-face{
11 | font-family: 'JetBrains Mono';
12 | src: url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/web/JetBrainsMono-Bold.eot') format('embedded-opentype'),
13 | url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/webfonts/JetBrainsMono-Bold.woff2') format('woff2'),
14 | url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/fonts/ttf/JetBrainsMono-Bold.ttf') format('truetype');
15 | font-weight: bold;
16 | font-style: bold;
17 | }
--------------------------------------------------------------------------------
/auth/src/commonMain/kotlin/inkapplications/shade/auth/BridgeAuth.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.auth
2 |
3 | import inkapplications.shade.auth.structures.AppId
4 | import inkapplications.shade.structures.AuthToken
5 | import kotlin.time.Duration
6 | import kotlin.time.Duration.Companion.seconds
7 | import kotlin.time.ExperimentalTime
8 |
9 | /**
10 | * Handles retrieving a token for authorization with the Hue Bridge.
11 | */
12 | interface BridgeAuth {
13 | /**
14 | * Wait for the user to hit the confirmation button to get a token.
15 | *
16 | * @param retries How many times to ask the hue bridge for a token
17 | * before giving up and timing out. (Default 50)
18 | * @param timeout The amount of time to wait in-between requests. (Default 5 seconds)
19 | * @return A bearer token to be used with requests to the Hue API.
20 | * These do not appear to expire. Store it safely.
21 | */
22 | @ExperimentalTime
23 | suspend fun awaitToken(
24 | appId: AppId,
25 | retries: Int = 50,
26 | timeout: Duration = 5.seconds
27 | ): AuthToken
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/parameters/DynamicsParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.parameters
2 |
3 | import inkapplications.shade.serialization.FractionalPercentageSerializer
4 | import inkapplications.shade.serialization.MillisecondDurationSerializer
5 | import inkapplications.spondee.scalar.Percentage
6 | import kotlinx.serialization.Serializable
7 | import kotlin.time.Duration
8 |
9 | /**
10 | * Settings for dynamics during a light setting change
11 | */
12 | @Serializable
13 | data class DynamicsParameters(
14 | /**
15 | * Duration of a light transition or timed effects in ms.
16 | */
17 | @Serializable(with = MillisecondDurationSerializer::class)
18 | val duration: Duration? = null,
19 |
20 | /**
21 | * speed of dynamic palette or effect.
22 | *
23 | * The speed is valid for the dynamic palette if the status is
24 | * `dynamic_palette` or for the corresponding effect listed in status.
25 | * In case of status `none`, the speed is not valid
26 | */
27 | @Serializable(with = FractionalPercentageSerializer::class)
28 | val speed: Percentage? = null,
29 | )
30 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/HueConfigurationContainer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.coroutines.flow.StateFlow
4 |
5 | /**
6 | * Store configuration for communicating with the Hue API.
7 | */
8 | interface HueConfigurationContainer {
9 | /**
10 | * The Hue IP or hostname to communicate with.
11 | */
12 | val hostname: StateFlow
13 |
14 | /**
15 | * TLS configuration strategy.
16 | */
17 | val securityStrategy: StateFlow
18 |
19 | /**
20 | * Auth token used in authenticated requests to the hue bridge
21 | */
22 | val authToken: StateFlow
23 |
24 | /**
25 | * he Hue IP or hostname to communicate with. eg "192.168.1.5"
26 | */
27 | suspend fun setHostname(hostname: String?)
28 |
29 | /**
30 | * Set the application key/token to use when communicating with the Hue bridge.
31 | */
32 | suspend fun setAuthToken(token: AuthToken?)
33 |
34 | /**
35 | * TLS configuration strategy.
36 | */
37 | suspend fun setSecurityStrategy(securityStrategy: SecurityStrategy)
38 | }
39 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/LightControls.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights
2 |
3 | import inkapplications.shade.lights.parameters.LightUpdateParameters
4 | import inkapplications.shade.lights.structures.Light
5 | import inkapplications.shade.structures.ResourceId
6 | import inkapplications.shade.structures.ResourceReference
7 |
8 | /**
9 | * Actions to get and control lighting state.
10 | */
11 | interface LightControls {
12 | /**
13 | * Get the state of a single light
14 | *
15 | * @param id The v2 unique ID of the light to fetch information for.
16 | */
17 | suspend fun getLight(id: ResourceId): Light
18 |
19 | /**
20 | * Get a list of all lights connected to the bridge
21 | */
22 | suspend fun listLights(): List
23 |
24 | /**
25 | * Update a light's state.
26 | *
27 | * @param id The v2 unique ID of the light to fetch information for.
28 | * @param parameters State to be changed on the light.
29 | * @return A reference to the updated resource.
30 | */
31 | suspend fun updateLight(id: ResourceId, parameters: LightUpdateParameters): ResourceReference
32 | }
33 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/devices/DeviceOutput.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.devices
2 |
3 | import com.github.ajalt.clikt.core.CliktCommand
4 | import com.github.ajalt.clikt.output.TermUi
5 | import inkapplications.shade.devices.structures.Device
6 |
7 | /**
8 | * Echo a device's properties in a human readable format.
9 | */
10 | fun CliktCommand.echoDevice(device: Device) {
11 | echo("${device.id.value}:")
12 | echo(" Name: ${device.metadata.name}")
13 | echo(" Archetype: ${device.metadata.archetype}")
14 | echo(" Model ID: ${device.productData.modelId}")
15 | echo(" Manufacturer: ${device.productData.manufacturerName}")
16 | echo(" Product Name: ${device.productData.productName}")
17 | echo(" Product Archetype: ${device.productData.productArchetype}")
18 | echo(" Certified: ${device.productData.certified}")
19 | echo(" Software Version: ${device.productData.softwareVersion}")
20 | device.productData.hardwarePlatformType?.run {
21 | echo(" Hardware Platform Type: $this")
22 | }
23 | echo(" Services:")
24 | device.services.forEach {
25 | echo(" - $it")
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internals/src/commonMain/kotlin/inkapplications/shade/internals/DummyConfigurationContainer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.structures.AuthToken
4 | import inkapplications.shade.structures.HueConfigurationContainer
5 | import inkapplications.shade.structures.SecurityStrategy
6 | import kotlinx.coroutines.flow.MutableStateFlow
7 | import kotlinx.coroutines.flow.StateFlow
8 |
9 | /**
10 | * An empty implementation of the configuration container.
11 | *
12 | * This can be useful either for testing or for scenarios where you do not
13 | * want the configurations to be saved.
14 | */
15 | object DummyConfigurationContainer: HueConfigurationContainer {
16 | override val hostname: StateFlow = MutableStateFlow(null)
17 | override val securityStrategy: StateFlow = MutableStateFlow(SecurityStrategy.PlatformTrust)
18 | override val authToken: StateFlow = MutableStateFlow(null)
19 | override suspend fun setHostname(hostname: String?) = Unit
20 | override suspend fun setAuthToken(token: AuthToken?) = Unit
21 | override suspend fun setSecurityStrategy(securityStrategy: SecurityStrategy) = Unit
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/pushes.yml:
--------------------------------------------------------------------------------
1 | name: Latest Build
2 | on:
3 | push:
4 | branches: [master]
5 | jobs:
6 | tests:
7 | uses: inkapplications/.github/.github/workflows/kmp-checks.yml@1.2.0
8 | build:
9 | needs: [tests]
10 | runs-on: ubuntu-latest
11 | steps:
12 | -
13 | name: Checkout
14 | uses: actions/checkout@v5.0.0
15 | -
16 | name: Assemble
17 | run: ./gradlew assembleDist
18 | -
19 | name: Prepare Archives
20 | run: cp cli/build/distributions/shade-*.zip cli/build/distributions/shade.zip && cp cli/build/distributions/shade-*.tar cli/build/distributions/shade.tar
21 | -
22 | name: Archive CLI Tar
23 | uses: actions/upload-artifact@v4.6.2
24 | with:
25 | name: shade.tar
26 | path: cli/build/distributions/shade.tar
27 | -
28 | name: Archive CLI Zip
29 | uses: actions/upload-artifact@v4.6.2
30 | with:
31 | name: shade.zip
32 | path: cli/build/distributions/shade.zip
33 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/structures/Device.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.structures
2 |
3 | import inkapplications.shade.structures.ResourceId
4 | import inkapplications.shade.structures.ResourceReference
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Attributes for a device configured on the hue bridge.
10 | */
11 | @Serializable
12 | data class Device(
13 | /**
14 | * Unique ID for the device.
15 | */
16 | val id: ResourceId,
17 |
18 | /**
19 | * Clip v1 resource identifier.
20 | */
21 | @Deprecated("V1 Resource. Left for migration purposes only, may be removed at any point by API or SDK.")
22 | @SerialName("id_v1")
23 | val v1Id: String? = null,
24 |
25 | /**
26 | * Information about the hardware itself.
27 | */
28 | @SerialName("product_data")
29 | val productData: ProductData,
30 |
31 | /**
32 | * Configured metadata for this device.
33 | */
34 | val metadata: ProductMetadata,
35 |
36 | /**
37 | * References all services providing control and state of the device.
38 | */
39 | val services: List,
40 | )
41 |
--------------------------------------------------------------------------------
/internals/src/commonMain/kotlin/inkapplications/shade/internals/InternalsModule.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import inkapplications.shade.structures.HueConfigurationContainer
4 | import kimchi.logger.EmptyLogger
5 | import kimchi.logger.KimchiLogger
6 | import kotlinx.serialization.json.Json
7 |
8 | /**
9 | * Provides access to services in the internals module.
10 | *
11 | * @param configurationContainer Container for storing Host/Token information on the API.
12 | * @param logger Logger used in network and internal operations
13 | */
14 | class InternalsModule(
15 | val configurationContainer: HueConfigurationContainer,
16 | logger: KimchiLogger = EmptyLogger,
17 | ) {
18 | val json = Json {
19 | ignoreUnknownKeys = true
20 | }
21 | val platformModule = PlatformModule(configurationContainer, json, logger)
22 | private val configurableHttpClient = ConfigurableHttpClient(
23 | platformModule = platformModule,
24 | configurationContainer = configurationContainer,
25 | json = json,
26 | logger = logger,
27 | )
28 |
29 | /**
30 | * HttpClient initialized to be configured by the [configurationContainer].
31 | */
32 | val hueHttpClient: HueHttpClient = configurableHttpClient
33 | }
34 |
--------------------------------------------------------------------------------
/docs/css/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | footer, header, hgroup, menu, nav, section {
29 | display: block;
30 | }
31 | body {
32 | line-height: 1;
33 | }
34 | ol, ul {
35 | list-style: none;
36 | }
37 | blockquote, q {
38 | quotes: none;
39 | }
40 | blockquote:before, blockquote:after,
41 | q:before, q:after {
42 | content: '';
43 | content: none;
44 | }
45 | table {
46 | border-collapse: collapse;
47 | border-spacing: 0;
48 | }
49 |
--------------------------------------------------------------------------------
/docs/css/main-2.0.css:
--------------------------------------------------------------------------------
1 | @import url("https://assets.inkapplications.com/css/main-v1.css");
2 | @import url("https://fonts.googleapis.com/css?family=Alegreya+Sans+SC:400");
3 |
4 | :root
5 | {
6 | --color-accent: #ec6238;
7 | }
8 |
9 | .logo
10 | {
11 | margin: 0 auto;
12 | height: 4rem;
13 | display: block;
14 | }
15 |
16 | section
17 | {
18 | border: 1px solid transparent;
19 | }
20 | section:has(header > a:target)
21 | {
22 | border: 1px solid var(--color-accent);
23 | }
24 |
25 | h1,
26 | h2,
27 | h3
28 | {
29 | font-family: 'Alegreya Sans SC', monospace;
30 | font-weight: 400;
31 | }
32 |
33 | section > *:last-child
34 | {
35 | margin-bottom: 0;
36 | }
37 |
38 | section nav ul
39 | {
40 | margin: 1.5rem 0;
41 | }
42 |
43 | section nav ul > li
44 | {
45 | margin: .75rem 0;
46 | }
47 |
48 | section nav ul > li::before
49 | {
50 | content: none;
51 | }
52 |
53 | section nav a:not(.button):after
54 | {
55 | content: ' >';
56 | }
57 |
58 |
59 | @media not (prefers-color-scheme: dark) {
60 | .logo.only-darkmode
61 | {
62 | display: none;
63 | }
64 | }
65 | @media (prefers-color-scheme: dark) {
66 | .logo.only-lightmode
67 | {
68 | display: none;
69 | }
70 | }
71 |
72 | .button
73 | {
74 | display: inline-block;
75 | }
76 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/scenes/SceneOutput.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.scenes
2 |
3 | import com.github.ajalt.clikt.core.CliktCommand
4 | import com.github.ajalt.clikt.output.TermUi
5 | import inkapplications.shade.scenes.structures.Scene
6 | import inkapplications.spondee.scalar.toWholePercentage
7 | import inkapplications.spondee.structure.format
8 |
9 | fun CliktCommand.echoScene(scene: Scene) {
10 | echo("${scene.id}:")
11 | echo(" Name: ${scene.metadata.name}")
12 | echo(" Group: ${scene.group}")
13 | echo(" Speed: ${scene.speed.toWholePercentage().format()}")
14 | echo(" Auto Dynamic: ${scene.autoDynamic}")
15 | echo(" Actions:")
16 | scene.actions.forEach { action ->
17 | echo(" - ${action.target}:")
18 | echo(" Power: ${action.action.powerValue}")
19 | echo(" Brightness: ${action.action.dimmingValue?.brightness?.toWholePercentage()?.format()}")
20 | echo(" Color: ${action.action.colorValue?.color?.toSRGB()?.toHex()}")
21 | echo(" Color Temperature: ${action.action.colorTemperatureValue?.temperature?.toKelvin()?.format()}")
22 | echo(" Effect: ${action.action.effect}")
23 | echo(" Duration: ${action.action.dynamicsDuration}")
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/ShadeLights.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights
2 |
3 | import inkapplications.shade.internals.HueHttpClient
4 | import inkapplications.shade.internals.getData
5 | import inkapplications.shade.internals.putData
6 | import inkapplications.shade.lights.parameters.LightUpdateParameters
7 | import inkapplications.shade.lights.structures.Light
8 | import inkapplications.shade.structures.ResourceId
9 | import inkapplications.shade.structures.ResourceReference
10 |
11 | /**
12 | * Implements lighting controls via the hue client
13 | */
14 | internal class ShadeLights(
15 | private val hueClient: HueHttpClient,
16 | ): LightControls {
17 | override suspend fun getLight(id: ResourceId): Light {
18 | return hueClient.getData>("resource", "light", id.value).single()
19 | }
20 |
21 | override suspend fun listLights(): List {
22 | return hueClient.getData("resource", "light")
23 | }
24 |
25 | override suspend fun updateLight(id: ResourceId, parameters: LightUpdateParameters): ResourceReference {
26 | val response: List = hueClient.putData(
27 | body = parameters,
28 | pathSegments = arrayOf("resource", "light", id.value),
29 | )
30 |
31 | return response.single()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/zones/src/commonMain/kotlin/inkapplications/shade/zones/structures/Zone.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.zones.structures
2 |
3 | import inkapplications.shade.structures.ResourceId
4 | import inkapplications.shade.structures.ResourceReference
5 | import inkapplications.shade.structures.SegmentMetadata
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * State and capabilities of a zone resource.
10 | */
11 | @Serializable
12 | data class Zone(
13 | /**
14 | * Unique v2 ID for the zone.
15 | */
16 | val id: ResourceId,
17 |
18 | /**
19 | * References all services aggregating control and state of children
20 | * in the group.
21 | *
22 | * This includes all services grouped in the group hierarchy given by
23 | * child relation This includes all services of a device grouped in the
24 | * group hierarchy given by child relation Aggregation is per service type,
25 | * ie. every service type which can be grouped has a corresponding
26 | * definition of grouped type Supported types "light"
27 | */
28 | val services: List,
29 |
30 | /**
31 | * Configuration metatdata for the room/zone type.
32 | */
33 | val metadata: SegmentMetadata,
34 |
35 | /**
36 | * Children devices of this zone.
37 | */
38 | val children: List,
39 | )
40 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/LightDynamics.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import inkapplications.shade.serialization.FractionalPercentageSerializer
4 | import inkapplications.spondee.scalar.Percentage
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * Dynamic Lighting Effects information
10 | */
11 | @Serializable
12 | data class LightDynamics(
13 | /**
14 | * Current status of the lamp with dynamics.
15 | */
16 | val status: DynamicsStatus,
17 |
18 | /**
19 | * Statuses in which a lamp could be when playing dynamics.
20 | */
21 | @SerialName("status_values")
22 | val statusValues: List,
23 |
24 | /**
25 | * Speed of dynamic palette or effect.
26 | *
27 | * The speed is valid for the dynamic palette if the status is
28 | * [DynamicsStatus.DynamicPalette] or for the corresponding effect listed
29 | * in status.
30 | * In case of status [DynamicsStatus.None], the speed is not valid
31 | */
32 | @Serializable(with = FractionalPercentageSerializer::class)
33 | val speed: Percentage,
34 |
35 | /**
36 | * Indicates whether the value presented in speed is valid
37 | */
38 | @SerialName("speed_valid")
39 | val speedValid: Boolean,
40 | )
41 |
--------------------------------------------------------------------------------
/rooms/src/commonMain/kotlin/inkapplications/shade/rooms/structures/Room.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.rooms.structures
2 |
3 | import inkapplications.shade.structures.ResourceId
4 | import inkapplications.shade.structures.ResourceReference
5 | import inkapplications.shade.structures.SegmentMetadata
6 | import kotlinx.serialization.Serializable
7 |
8 | /**
9 | * State and capabilities of a room resource.
10 | */
11 | @Serializable
12 | data class Room(
13 | /**
14 | * Unique identifier representing a specific room instance.
15 | */
16 | val id: ResourceId,
17 |
18 | /**
19 | * References all services aggregating control and state of children
20 | * in the group.
21 | *
22 | * This includes all services grouped in the group hierarchy given by child
23 | * relation This includes all services of a device grouped in the group
24 | * hierarchy given by child relation Aggregation is per service type,
25 | * ie every service type which can be grouped has a corresponding definition
26 | * of grouped type Supported types “light”
27 | */
28 | val services: List,
29 |
30 | /**
31 | * Configuration object for a room.
32 | */
33 | val metadata: SegmentMetadata,
34 |
35 | /**
36 | * Devices to group by the Room.
37 | */
38 | val children: List,
39 | )
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Shade
2 |
3 | 
4 |
5 | _Cross Platform CLI and Multiplatform Kotlin SDK for controlling Hue devices._
6 |
7 | ## Cross-Platform Command Line
8 |
9 | Shade's CLI application for Windows, MacOS and Linux provides commands for
10 | controlling your lighing directly from the terminal.
11 |
12 | ```shell
13 | $ shade update-light $lightId --brightness=10%
14 | ```
15 |
16 | [Get Started](https://shade.lighting)
17 |
18 | ## Kotlin Multiplatform SDK
19 |
20 | Shade's Kotlin SDK provides API's for controlling your lighting on Java,
21 | Android and Javascript platforms.
22 |
23 | ```kotlin
24 | shade.lights.updateLight(
25 | id = lightId,
26 | parameters = LightUpdateParameters(
27 | brightness = 10.percent,
28 | ),
29 | )
30 | ```
31 |
32 | [Get Started](https://shade.lighting)
33 |
34 | ## Free + Open Source
35 | Shade is free under the [MIT License], the project is Open Source, actively
36 | maintained, and always looking for contributions.
37 |
38 | There are many Hue devices and things to do with them. Testing all that can
39 | be difficult. Please [report] any issues you find.
40 |
41 | [MIT License]:LICENSE
42 | [report]:https://github.com/InkApplications/Shade/issues/new/choose
43 |
44 | ## Trademarks
45 |
46 | "Hue" and "Philips" are trademarks of Signify Holding and are not affiliated
47 | with this project.
48 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/lights/LightOutput.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.lights
2 |
3 | import com.github.ajalt.clikt.core.CliktCommand
4 | import com.github.ajalt.clikt.output.TermUi
5 | import inkapplications.shade.lights.structures.Light
6 | import inkapplications.spondee.scalar.toWholePercentage
7 | import inkapplications.spondee.structure.format
8 |
9 | fun CliktCommand.echoLight(light: Light) {
10 | echo("${light.id.value}:")
11 | echo(" On: ${light.powerInfo.on}")
12 | echo(" Mode: ${light.mode}")
13 | light.colorTemperatureInfo?.run {
14 | val temperatureString = temperature?.toKelvin()?.format() ?: "--"
15 | echo(" Temperature: $temperatureString")
16 | echo(" Temperature Range: ${range}")
17 | }
18 | light.dimmingInfo?.run {
19 | echo(" Brightness: ${brightness.toWholePercentage().format()}")
20 | }
21 | light.colorInfo?.run {
22 | echo(" Color (rgb): ${color.toSRGB().toHex()}")
23 | echo(" Color (xy): [${color.toXYZ().toCIExyY().x},${color.toXYZ().toCIExyY().y}]")
24 | }
25 | light.dynamics?.run {
26 | echo(" Current Dynamics: ${status}")
27 | echo(" Available Dynamics: ${statusValues.joinToString()}")
28 | echo(" Dynamics Speed: ${speed.toWholePercentage().format()}${"*".takeUnless { speedValid }.orEmpty()}")
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/grouped-lights/src/commonMain/kotlin/inkapplications/shade/groupedlights/structures/GroupedLight.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.groupedlights.structures
2 |
3 | import inkapplications.shade.lights.structures.AlertInfo
4 | import inkapplications.shade.structures.PowerInfo
5 | import inkapplications.shade.structures.ResourceId
6 | import inkapplications.shade.structures.ResourceReference
7 | import kotlinx.serialization.SerialName
8 | import kotlinx.serialization.Serializable
9 |
10 | /**
11 | * A configured light group for a room, zone or home.
12 | */
13 | @Serializable
14 | data class GroupedLight(
15 | /**
16 | * The V2 unique identifier for the group.
17 | */
18 | val id: ResourceId,
19 |
20 | /**
21 | * Owner of the service
22 | *
23 | * In case the owner service is deleted, the service also gets deleted.
24 | */
25 | val owner: ResourceReference,
26 |
27 | /**
28 | * Information about the power-state of the light group.
29 | */
30 | @SerialName("on")
31 | val powerInfo: PowerInfo? = null,
32 |
33 | /**
34 | * Information about the group's dimming, if supported.
35 | */
36 | @SerialName("dimming")
37 | val dimmingInfo: GroupDimmingInfo? = null,
38 |
39 | /**
40 | * Joined alert control for the light group.
41 | */
42 | @SerialName("alert")
43 | val alertInfo: AlertInfo? = null,
44 | )
45 |
--------------------------------------------------------------------------------
/grouped-lights/src/commonMain/kotlin/inkapplications/shade/groupedlights/ShadeGroupedLights.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.groupedlights
2 |
3 | import inkapplications.shade.groupedlights.parameters.GroupedLightUpdateParameters
4 | import inkapplications.shade.groupedlights.structures.GroupedLight
5 | import inkapplications.shade.internals.HueHttpClient
6 | import inkapplications.shade.internals.getData
7 | import inkapplications.shade.internals.putData
8 | import inkapplications.shade.structures.ResourceId
9 | import inkapplications.shade.structures.ResourceReference
10 |
11 | internal class ShadeGroupedLights(
12 | private val hueHttpClient: HueHttpClient,
13 | ): GroupedLightControls {
14 | override suspend fun listGroups(): List {
15 | return hueHttpClient.getData("resource", "grouped_light")
16 | }
17 |
18 | override suspend fun getGroup(id: ResourceId): GroupedLight {
19 | return hueHttpClient.getData>("resource", "grouped_light", id.value).single()
20 | }
21 |
22 | override suspend fun updateGroup(id: ResourceId, parameters: GroupedLightUpdateParameters): ResourceReference {
23 | val response: List = hueHttpClient.putData(
24 | body = parameters,
25 | pathSegments = arrayOf("resource", "grouped_light", id.value),
26 | )
27 |
28 | return response.single()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/grouped-lights/src/commonMain/kotlin/inkapplications/shade/groupedlights/GroupedLightControls.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.groupedlights
2 |
3 | import inkapplications.shade.groupedlights.parameters.GroupedLightUpdateParameters
4 | import inkapplications.shade.groupedlights.structures.GroupedLight
5 | import inkapplications.shade.structures.ResourceId
6 | import inkapplications.shade.structures.ResourceReference
7 |
8 | /**
9 | * Actions to get and control grouped lights.
10 | */
11 | interface GroupedLightControls {
12 | /**
13 | * Get a list of the grouped lights configured on the Hue bridge
14 | *
15 | * @return A list of all grouped lights configured on the bridge.
16 | */
17 | suspend fun listGroups(): List
18 |
19 | /**
20 | * Get details about a single light group by ID.
21 | *
22 | * @param id The v2 unique id of the grouped light to fetch information for.
23 | * @return State and capability information about the grouped light.
24 | */
25 | suspend fun getGroup(id: ResourceId): GroupedLight
26 |
27 | /**
28 | * Update the state or details of a grouped light
29 | *
30 | * @param id The v2 unique ID of the grouped light to be updated.
31 | * @return a Reference to the updated grouped light
32 | */
33 | suspend fun updateGroup(id: ResourceId, parameters: GroupedLightUpdateParameters): ResourceReference
34 | }
35 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/ColorTemperatureRange.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import inkapplications.spondee.measure.ColorTemperature
4 | import inkapplications.spondee.measure.toMireds
5 | import inkapplications.spondee.structure.convert
6 | import kotlin.math.roundToInt
7 | import kotlin.math.roundToLong
8 |
9 | /**
10 | * Represents a range of color temperatures.
11 | */
12 | data class ColorTemperatureRange(
13 | val coolest: ColorTemperature,
14 | val warmest: ColorTemperature,
15 | ) {
16 | /**
17 | * Express the color temperatures as a range of Kelvin units.
18 | */
19 | val kelvinRange = warmest.toKelvin().convert { roundToLong() }..coolest.toKelvin().convert { roundToLong() }
20 |
21 | /**
22 | * Express the color temperatures as a range of Mireds.
23 | */
24 | val miredRange = coolest.toMireds().convert { roundToInt() }..warmest.toMireds().convert { roundToInt() }
25 |
26 | operator fun contains(other: ColorTemperature): Boolean {
27 | val warmestKelvin = warmest.toKelvin().value.toDouble()
28 | val coolestKelvin = coolest.toKelvin().value.toDouble()
29 | val otherKelvin = other.toKelvin().value.toDouble()
30 |
31 | return otherKelvin in warmestKelvin..coolestKelvin
32 | }
33 |
34 | override fun toString(): String {
35 | return "$warmest..$coolest"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/GamutType.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlin.jvm.JvmInline
5 |
6 | /**
7 | * Simple Gamut Type definition used by Hue products.
8 | */
9 | @Serializable
10 | @JvmInline
11 | value class GamutType(val key: String) {
12 | override fun toString(): String = key
13 |
14 | companion object {
15 | /**
16 | * Gamut of early Philips color-only products
17 | */
18 | val A = GamutType("A")
19 |
20 | /**
21 | * Limited gamut of first Hue color products
22 | */
23 | val B = GamutType("B")
24 |
25 | /**
26 | * Richer color gamut of Hue white and color ambiance products
27 | */
28 | val C = GamutType("C")
29 |
30 | /**
31 | * Color gamut of non-hue products with non-hue gamuts resp w/o gamut
32 | */
33 | val Other = GamutType("other")
34 |
35 | @Deprecated("This is an unbounded set of values. The values provided here are not exhaustive and will be removed in a future release.")
36 | fun values(): Array = arrayOf(A, B, C, Other)
37 |
38 | @Deprecated(
39 | message = "Deprecated in favor of constructor",
40 | replaceWith = ReplaceWith("GamutType(key)"),
41 | )
42 | fun valueOf(key: String) = values().single { it.key == key }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/discover/src/commonMain/kotlin/inkapplications/shade/discover/KtorDiscovery.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.discover
2 |
3 | import inkapplications.shade.discover.structures.Bridge
4 | import inkapplications.shade.structures.ApiStatusError
5 | import inkapplications.shade.structures.NetworkException
6 | import inkapplications.shade.structures.SerializationError
7 | import io.ktor.client.*
8 | import io.ktor.client.call.*
9 | import io.ktor.client.request.*
10 | import io.ktor.http.*
11 |
12 | /**
13 | * Implements bridge discovery using a local ktor client
14 | */
15 | internal class KtorDiscovery(
16 | private val client: HttpClient,
17 | ): BridgeDiscovery {
18 | override suspend fun getDevices(): List {
19 | val httpResponse = try {
20 | client.get {
21 | url {
22 | host = "discovery.meethue.com"
23 | protocol = URLProtocol.HTTPS
24 | }
25 | accept(ContentType.Application.Json)
26 | }
27 | } catch (e: Throwable) {
28 | throw NetworkException("Unknown Error making API Request for discovery", e)
29 | }
30 |
31 | if (!httpResponse.status.isSuccess()) throw ApiStatusError(
32 | code = httpResponse.status.value,
33 | )
34 |
35 | return try {
36 | httpResponse.body()
37 | } catch (e: Throwable) {
38 | throw SerializationError("Unable to parse discovery response", e)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/grouped-lights/src/commonMain/kotlin/inkapplications/shade/groupedlights/events/GroupedLightEvent.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.groupedlights.events
2 |
3 | import inkapplications.shade.lights.structures.AlertInfo
4 | import inkapplications.shade.lights.structures.DimmingInfo
5 | import inkapplications.shade.structures.PowerInfo
6 | import inkapplications.shade.structures.ResourceId
7 | import inkapplications.shade.structures.ResourceReference
8 | import inkapplications.shade.structures.UndocumentedApi
9 | import kotlinx.serialization.SerialName
10 | import kotlinx.serialization.Serializable
11 |
12 | /**
13 | * Data sent for a light group event.
14 | */
15 | @Serializable
16 | @UndocumentedApi
17 | data class GroupedLightEvent(
18 | /**
19 | * The V2 unique identifier for the group.
20 | */
21 | val id: ResourceId,
22 |
23 | /**
24 | * Owner of the service
25 | *
26 | * In case the owner service is deleted, the service also gets deleted.
27 | */
28 | val owner: ResourceReference,
29 |
30 | /**
31 | * Information about the power-state of the light group.
32 | */
33 | @SerialName("on")
34 | val powerInfo: PowerInfo? = null,
35 |
36 | /**
37 | * Information about the group's dimming, if supported.
38 | */
39 | @SerialName("dimming")
40 | val dimmingInfo: DimmingInfo? = null,
41 |
42 | /**
43 | * Joined alert control for the light group.
44 | */
45 | @SerialName("alert")
46 | val alertInfo: AlertInfo? = null,
47 | )
48 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/devices/UpdateDeviceCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.devices
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import com.github.ajalt.clikt.parameters.options.convert
5 | import com.github.ajalt.clikt.parameters.options.option
6 | import inkapplications.shade.cli.AuthorizedShadeCommand
7 | import inkapplications.shade.cli.resourceId
8 | import inkapplications.shade.devices.parameters.DeviceMetadataParameters
9 | import inkapplications.shade.devices.parameters.UpdateDeviceParameters
10 | import inkapplications.shade.devices.structures.ProductArchetype
11 |
12 | object UpdateDeviceCommand: AuthorizedShadeCommand(
13 | help = "Update a device on the hue bridge",
14 | ) {
15 | private val deviceId by argument().resourceId()
16 | private val name by option(help = "Human readable name to assign to the device")
17 | private val archetype by option(help = "The product type of device to assign to the device")
18 | .convert { ProductArchetype(it) }
19 |
20 | override suspend fun runCommand(): Int {
21 | val response = shade.devices.updateDevice(
22 | deviceId = deviceId,
23 | parameters = UpdateDeviceParameters(
24 | metadata = DeviceMetadataParameters(
25 | name = name,
26 | archetype = archetype,
27 | ),
28 | ),
29 | )
30 |
31 | logger.debug("Got response: $response")
32 |
33 | return 0
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/devices/src/commonMain/kotlin/inkapplications/shade/devices/structures/ProductData.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.devices.structures
2 |
3 | import inkapplications.shade.structures.VersionString
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * Manufacturer specified information about a device.
9 | */
10 | @Serializable
11 | data class ProductData(
12 | /**
13 | * Unique Identification of the device model.
14 | */
15 | @SerialName("model_id")
16 | val modelId: ModelId,
17 |
18 | /**
19 | * Name of the device's manufacturer.
20 | */
21 | @SerialName("manufacturer_name")
22 | val manufacturerName: String,
23 |
24 | /**
25 | * Name of the product.
26 | */
27 | @SerialName("product_name")
28 | val productName: String,
29 |
30 | /**
31 | * General archetype of the device, as specified by the manufacturer.
32 | */
33 | @SerialName("product_archetype")
34 | val productArchetype: ProductArchetype,
35 |
36 | /**
37 | * Whether the device is hue certified.
38 | */
39 | val certified: Boolean,
40 |
41 | /**
42 | * Version of the software running on the device.
43 | */
44 | @SerialName("software_version")
45 | val softwareVersion: VersionString,
46 |
47 | /**
48 | * Hardware type; identified by Manufacturer code and ImageType.
49 | */
50 | @SerialName("hardware_platform_type")
51 | val hardwarePlatformType: HardwarePlatformType? = null,
52 | )
53 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/rooms/CreateRoomCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.rooms
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import com.github.ajalt.clikt.parameters.options.option
5 | import inkapplications.shade.cli.AuthorizedShadeCommand
6 | import inkapplications.shade.cli.deviceResourceReferences
7 | import inkapplications.shade.cli.segmentArchetype
8 | import inkapplications.shade.rooms.parameters.RoomCreateParameters
9 | import inkapplications.shade.structures.SegmentMetadata
10 |
11 | object CreateRoomCommand: AuthorizedShadeCommand(
12 | help = "Create a new room on the Hue bridge"
13 | ) {
14 | val name by argument(
15 | help = "A human-readable name for the room"
16 | )
17 |
18 | val archetype by argument(
19 | help = "The type of room"
20 | ).segmentArchetype()
21 |
22 | val childrenDeviceIds by option(
23 | help = "A comma-separated list of device ID's to add as children for the room."
24 | ).deviceResourceReferences()
25 |
26 | override suspend fun runCommand(): Int {
27 | val response = shade.rooms.createRoom(
28 | parameters = RoomCreateParameters(
29 | metadata = SegmentMetadata(
30 | archetype = archetype,
31 | name = name,
32 | ),
33 | children = childrenDeviceIds.orEmpty(),
34 | )
35 | )
36 |
37 | logger.debug("Got response $response")
38 |
39 | return 0
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/cli/src/main/kotlin/inkapplications/shade/cli/zones/CreateZoneCommand.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.cli.zones
2 |
3 | import com.github.ajalt.clikt.parameters.arguments.argument
4 | import com.github.ajalt.clikt.parameters.options.option
5 | import inkapplications.shade.cli.AuthorizedShadeCommand
6 | import inkapplications.shade.cli.deviceResourceReferences
7 | import inkapplications.shade.cli.segmentArchetype
8 | import inkapplications.shade.structures.SegmentMetadata
9 | import inkapplications.shade.zones.parameters.ZoneCreateParameters
10 |
11 | object CreateZoneCommand: AuthorizedShadeCommand(
12 | help = "Create a new zone on the Hue bridge"
13 | ) {
14 | val name by argument(
15 | help = "A human-readable name for the zone"
16 | )
17 |
18 | val archetype by argument(
19 | help = "The type of zone"
20 | ).segmentArchetype()
21 |
22 | val childrenDeviceIds by option(
23 | help = "A comma-separated list of device ID's to add as children for the zone."
24 | ).deviceResourceReferences()
25 |
26 | override suspend fun runCommand(): Int {
27 | val response = shade.zones.createZone(
28 | parameters = ZoneCreateParameters(
29 | metadata = SegmentMetadata(
30 | archetype = archetype,
31 | name = name,
32 | ),
33 | children = childrenDeviceIds.orEmpty(),
34 | )
35 | )
36 |
37 | logger.debug("Got response $response")
38 |
39 | return 0
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/structures/src/commonMain/kotlin/inkapplications/shade/structures/InMemoryConfigurationContainer.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.structures
2 |
3 | import kotlinx.coroutines.flow.MutableStateFlow
4 | import kotlinx.coroutines.flow.StateFlow
5 |
6 | /**
7 | * Implements a configuration container by storing config in a thread-safe
8 | * memory storage structure.
9 | *
10 | * @param initialAuthToken Optional auth token to start with
11 | */
12 | class InMemoryConfigurationContainer(
13 | initialHostname: String? = null,
14 | initialAuthToken: AuthToken? = null,
15 | initialSecurityStrategy: SecurityStrategy = SecurityStrategy.PlatformTrust,
16 | ): HueConfigurationContainer {
17 | private val mutableHostname = MutableStateFlow(initialHostname)
18 | private val mutableAuthToken = MutableStateFlow(initialAuthToken)
19 | private val mutableSecurityStrategy = MutableStateFlow(initialSecurityStrategy)
20 | override val hostname: StateFlow = mutableHostname
21 | override val securityStrategy: StateFlow = mutableSecurityStrategy
22 | override val authToken: StateFlow = mutableAuthToken
23 |
24 | override suspend fun setHostname(hostname: String?) {
25 | mutableHostname.value = hostname
26 | }
27 |
28 | override suspend fun setAuthToken(token: AuthToken?) {
29 | mutableAuthToken.value = token
30 | }
31 |
32 | override suspend fun setSecurityStrategy(securityStrategy: SecurityStrategy) {
33 | mutableSecurityStrategy.value = securityStrategy
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/rooms/src/commonMain/kotlin/inkapplications/shade/rooms/RoomControls.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.rooms
2 |
3 | import inkapplications.shade.rooms.parameters.RoomCreateParameters
4 | import inkapplications.shade.rooms.parameters.RoomUpdateParameters
5 | import inkapplications.shade.rooms.structures.Room
6 | import inkapplications.shade.structures.ResourceId
7 | import inkapplications.shade.structures.ResourceReference
8 |
9 | /**
10 | * Actions to get and control rooms in the hue system
11 | */
12 | interface RoomControls {
13 | /**
14 | * Get the state of a single room
15 | *
16 | * @param id The v2 resource ID of the room to fetch data about
17 | */
18 | suspend fun getRoom(id: ResourceId): Room
19 |
20 | /**
21 | * Get a list of rooms configured on the hue service.
22 | */
23 | suspend fun listRooms(): List
24 |
25 | /**
26 | * Create a new room on the hue bridge.
27 | */
28 | suspend fun createRoom(parameters: RoomCreateParameters): ResourceReference
29 |
30 | /**
31 | * Update and existing room on the hue bridge.
32 | *
33 | * @param id The v2 resource ID of the room to be updated
34 | * @param parameters data about the room to be updated.
35 | */
36 | suspend fun updateRoom(id: ResourceId, parameters: RoomUpdateParameters): ResourceReference
37 |
38 | /**
39 | * Delete an existing room from the hue bridge.
40 | *
41 | * @param id The v2 resource ID of the room to be deleted
42 | */
43 | suspend fun deleteRoom(id: ResourceId): ResourceReference
44 | }
45 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/parameters/SceneUpdateParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.parameters
2 |
3 | import inkapplications.shade.scenes.structures.SceneActionReference
4 | import inkapplications.shade.scenes.structures.SceneMetadata
5 | import inkapplications.shade.scenes.structures.ScenePalette
6 | import inkapplications.shade.scenes.structures.SceneRecall
7 | import inkapplications.shade.serialization.FractionalPercentageSerializer
8 | import inkapplications.spondee.scalar.Percentage
9 | import kotlinx.serialization.SerialName
10 | import kotlinx.serialization.Serializable
11 |
12 | @Serializable
13 | data class SceneUpdateParameters(
14 | /**
15 | * Actions to be applied to each device in the scene's group.
16 | */
17 | val actions: List? = null,
18 |
19 | val recall: SceneRecall? = null,
20 |
21 | /**
22 | * User-supplied configuration info for the scene.
23 | */
24 | val metadata: SceneMetadata? = null,
25 |
26 | /**
27 | * Group of colors that describe the palette of colors used to be used
28 | * when playing dynamics.
29 | */
30 | val palette: ScenePalette? = null,
31 |
32 | /**
33 | * The speed of the dynamic palette.
34 | */
35 | @Serializable(with = FractionalPercentageSerializer::class)
36 | val speed: Percentage? = null,
37 |
38 | /**
39 | * Indicates whether to automatically start the scene dynamically on
40 | * active recall.
41 | */
42 | @SerialName("auto_dynamic")
43 | val autoDynamic: Boolean? = null,
44 | )
45 |
--------------------------------------------------------------------------------
/docs/reference/latest/ui-kit/assets/abstract-class.svg:
--------------------------------------------------------------------------------
1 |
4 |
23 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/events/LightEvent.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.events
2 |
3 | import inkapplications.shade.structures.PowerInfo
4 | import inkapplications.shade.structures.ResourceId
5 | import inkapplications.shade.structures.ResourceReference
6 | import inkapplications.shade.structures.UndocumentedApi
7 | import kotlinx.serialization.SerialName
8 | import kotlinx.serialization.Serializable
9 |
10 | /**
11 | * Data sent during a light update on the events API.
12 | */
13 | @Serializable
14 | @UndocumentedApi
15 | data class LightEvent(
16 | /**
17 | * Unique identifier representing a specific resource instance
18 | */
19 | val id: ResourceId,
20 |
21 | /**
22 | * Owner of the service
23 | *
24 | * In case the owner service is deleted, the service also gets deleted.
25 | */
26 | val owner: ResourceReference,
27 |
28 | /**
29 | * On/Off state of the light
30 | */
31 | @SerialName("on")
32 | val powerInfo: PowerInfo? = null,
33 |
34 | /**
35 | * Information about the light's dimming, if supported.
36 | */
37 | @SerialName("dimming")
38 | val dimmingInfo: DimmingInfoEvent? = null,
39 |
40 | /**
41 | * Information about the color temperature and capabilities of the light.
42 | */
43 | @SerialName("color_temperature")
44 | val colorTemperatureInfo: ColorTemperatureInfoEvent? = null,
45 |
46 | /**
47 | * Information about the bulb's color and color capabilities.
48 | */
49 | @SerialName("color")
50 | val colorInfo: ColorInfoEvent? = null,
51 | )
52 |
--------------------------------------------------------------------------------
/docs/reference/latest/images/nav-icons/abstract-class.svg:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/internals/src/commonMain/kotlin/inkapplications/shade/internals/CachedProperty.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.internals
2 |
3 | import kotlinx.coroutines.flow.MutableStateFlow
4 | import kotlin.properties.ReadOnlyProperty
5 | import kotlin.reflect.KProperty
6 |
7 | /**
8 | * Caches a property value until a key changes.
9 | *
10 | * @param keyProperty Function that provides the latest value of the cache key.
11 | * @param lazy Whether to initialize the value immediately, or upon first invocation.
12 | * @param factory Function to create the property value based on its current cache key.
13 | */
14 | class CachedProperty(
15 | val keyProperty: () -> KEY,
16 | lazy: Boolean = false,
17 | val factory: (KEY) -> VALUE,
18 | ): ReadOnlyProperty {
19 | private sealed interface State {
20 | class Null: State
21 | data class Set(val key: Any, val value: T): State
22 | }
23 |
24 | private val defaultState: State = if (lazy) State.Null() else create()
25 | private val cache: MutableStateFlow> = MutableStateFlow(defaultState)
26 |
27 | override fun getValue(thisRef: Any?, property: KProperty<*>): VALUE {
28 | return cache.value.let { currentState ->
29 | when {
30 | currentState is State.Set && currentState.key == keyProperty() -> currentState
31 | else -> create().also { cache.value = it }
32 | }
33 | }.value
34 | }
35 |
36 | private fun create(): State.Set = keyProperty().let { newKey ->
37 | State.Set(newKey, factory(newKey))
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lights/src/commonMain/kotlin/inkapplications/shade/lights/structures/Chromaticity.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.lights.structures
2 |
3 | import com.github.ajalt.colormath.Color
4 | import com.github.ajalt.colormath.model.XYZ
5 | import com.github.ajalt.colormath.model.xyY
6 | import inkapplications.shade.serialization.DelegateSerializer
7 | import kotlinx.serialization.Serializable
8 |
9 | /**
10 | * X/Y chromaticity to represent a color
11 | */
12 | @Serializable
13 | internal data class Chromaticity(
14 | val x: Float,
15 | val y: Float,
16 | ) {
17 | val xyY: xyY get() = xyY(x, y)
18 |
19 | /**
20 | * Serialize an xyY color as an X-Y chromaticity value
21 | */
22 | internal object CieChromaticitySerializer: DelegateSerializer(serializer()) {
23 | override fun serialize(data: xyY): Chromaticity = Chromaticity(x = data.x, y = data.y)
24 | override fun deserialize(data: Chromaticity): xyY = xyY(x = data.x, y = data.y)
25 | }
26 |
27 | /**
28 | * Serialize a color as an X-Y chromaticity value
29 | */
30 | internal object ColorSerializer: DelegateSerializer(serializer()) {
31 | override fun serialize(data: Color): Chromaticity {
32 | return Chromaticity(
33 | x = data.toXYZ().toCIExyY().x,
34 | y = data.toXYZ().toCIExyY().y,
35 | )
36 | }
37 |
38 | override fun deserialize(data: Chromaticity): Color {
39 | val chromaticity = xyY(data.x, data.y)
40 |
41 | return XYZ.invoke(chromaticity.X, chromaticity.Y, chromaticity.Z)
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/docs/reference/latest/package-list:
--------------------------------------------------------------------------------
1 | $dokka.format:html-v1
2 | $dokka.linkExtension:html
3 |
4 | module:auth
5 | inkapplications.shade.auth
6 | inkapplications.shade.auth.structures
7 | module:core
8 | inkapplications.shade.core
9 | module:devices
10 | inkapplications.shade.devices
11 | inkapplications.shade.devices.parameters
12 | inkapplications.shade.devices.structures
13 | module:discover
14 | inkapplications.shade.discover
15 | inkapplications.shade.discover.structures
16 | module:events
17 | inkapplications.shade.events
18 | shade.events
19 | module:grouped-lights
20 | inkapplications.shade.groupedlights
21 | inkapplications.shade.groupedlights.events
22 | inkapplications.shade.groupedlights.parameters
23 | inkapplications.shade.groupedlights.structures
24 | module:internals
25 | inkapplications.shade.internals
26 | module:lights
27 | inkapplications.shade.lights
28 | inkapplications.shade.lights.events
29 | inkapplications.shade.lights.parameters
30 | inkapplications.shade.lights.structures
31 | module:resources
32 | inkapplications.shade.resources
33 | inkapplications.shade.resources.structures
34 | module:rooms
35 | inkapplications.shade.rooms
36 | inkapplications.shade.rooms.parameters
37 | inkapplications.shade.rooms.structures
38 | module:scenes
39 | inkapplications.shade.scenes
40 | inkapplications.shade.scenes.parameters
41 | inkapplications.shade.scenes.structures
42 | module:serialization
43 | inkapplications.shade.serialization
44 | module:structures
45 | inkapplications.shade.structures
46 | inkapplications.shade.structures.parameters
47 | module:zones
48 | inkapplications.shade.zones
49 | inkapplications.shade.zones.parameters
50 | inkapplications.shade.zones.structures
51 |
--------------------------------------------------------------------------------
/docs/css/atom-one-dark.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Atom One Dark by Daniel Gamage
4 | Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
5 |
6 | base: #282c34
7 | mono-1: #abb2bf
8 | mono-2: #818896
9 | mono-3: #5c6370
10 | hue-1: #56b6c2
11 | hue-2: #61aeee
12 | hue-3: #c678dd
13 | hue-4: #98c379
14 | hue-5: #e06c75
15 | hue-5-2: #be5046
16 | hue-6: #d19a66
17 | hue-6-2: #e6c07b
18 |
19 | */
20 |
21 | .hljs {
22 | display: block;
23 | overflow-x: auto;
24 | padding: 0.5em;
25 | color: #abb2bf;
26 | background: #282c34;
27 | }
28 |
29 | .hljs-comment,
30 | .hljs-quote {
31 | color: #5c6370;
32 | font-style: italic;
33 | }
34 |
35 | .hljs-doctag,
36 | .hljs-keyword,
37 | .hljs-formula {
38 | color: #c678dd;
39 | }
40 |
41 | .hljs-section,
42 | .hljs-name,
43 | .hljs-selector-tag,
44 | .hljs-deletion,
45 | .hljs-subst {
46 | color: #e06c75;
47 | }
48 |
49 | .hljs-literal {
50 | color: #56b6c2;
51 | }
52 |
53 | .hljs-string,
54 | .hljs-regexp,
55 | .hljs-addition,
56 | .hljs-attribute,
57 | .hljs-meta-string {
58 | color: #98c379;
59 | }
60 |
61 | .hljs-built_in,
62 | .hljs-class .hljs-title {
63 | color: #e6c07b;
64 | }
65 |
66 | .hljs-attr,
67 | .hljs-variable,
68 | .hljs-template-variable,
69 | .hljs-type,
70 | .hljs-selector-class,
71 | .hljs-selector-attr,
72 | .hljs-selector-pseudo,
73 | .hljs-number {
74 | color: #d19a66;
75 | }
76 |
77 | .hljs-symbol,
78 | .hljs-bullet,
79 | .hljs-link,
80 | .hljs-meta,
81 | .hljs-selector-id,
82 | .hljs-title {
83 | color: #61aeee;
84 | }
85 |
86 | .hljs-emphasis {
87 | font-style: italic;
88 | }
89 |
90 | .hljs-strong {
91 | font-weight: bold;
92 | }
93 |
94 | .hljs-link {
95 | text-decoration: underline;
96 | }
97 |
--------------------------------------------------------------------------------
/docs/css/atom-one-light.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Atom One Light by Daniel Gamage
4 | Original One Light Syntax theme from https://github.com/atom/one-light-syntax
5 |
6 | base: #fafafa
7 | mono-1: #383a42
8 | mono-2: #686b77
9 | mono-3: #a0a1a7
10 | hue-1: #0184bb
11 | hue-2: #4078f2
12 | hue-3: #a626a4
13 | hue-4: #50a14f
14 | hue-5: #e45649
15 | hue-5-2: #c91243
16 | hue-6: #986801
17 | hue-6-2: #c18401
18 |
19 | */
20 |
21 | .hljs {
22 | display: block;
23 | overflow-x: auto;
24 | padding: 0.5em;
25 | color: #383a42;
26 | background: #fafafa;
27 | }
28 |
29 | .hljs-comment,
30 | .hljs-quote {
31 | color: #a0a1a7;
32 | font-style: italic;
33 | }
34 |
35 | .hljs-doctag,
36 | .hljs-keyword,
37 | .hljs-formula {
38 | color: #a626a4;
39 | }
40 |
41 | .hljs-section,
42 | .hljs-name,
43 | .hljs-selector-tag,
44 | .hljs-deletion,
45 | .hljs-subst {
46 | color: #e45649;
47 | }
48 |
49 | .hljs-literal {
50 | color: #0184bb;
51 | }
52 |
53 | .hljs-string,
54 | .hljs-regexp,
55 | .hljs-addition,
56 | .hljs-attribute,
57 | .hljs-meta-string {
58 | color: #50a14f;
59 | }
60 |
61 | .hljs-built_in,
62 | .hljs-class .hljs-title {
63 | color: #c18401;
64 | }
65 |
66 | .hljs-attr,
67 | .hljs-variable,
68 | .hljs-template-variable,
69 | .hljs-type,
70 | .hljs-selector-class,
71 | .hljs-selector-attr,
72 | .hljs-selector-pseudo,
73 | .hljs-number {
74 | color: #986801;
75 | }
76 |
77 | .hljs-symbol,
78 | .hljs-bullet,
79 | .hljs-link,
80 | .hljs-meta,
81 | .hljs-selector-id,
82 | .hljs-title {
83 | color: #4078f2;
84 | }
85 |
86 | .hljs-emphasis {
87 | font-style: italic;
88 | }
89 |
90 | .hljs-strong {
91 | font-weight: bold;
92 | }
93 |
94 | .hljs-link {
95 | text-decoration: underline;
96 | }
97 |
--------------------------------------------------------------------------------
/scenes/src/commonMain/kotlin/inkapplications/shade/scenes/parameters/SceneCreateParameters.kt:
--------------------------------------------------------------------------------
1 | package inkapplications.shade.scenes.parameters
2 |
3 | import inkapplications.shade.scenes.structures.SceneActionReference
4 | import inkapplications.shade.scenes.structures.SceneMetadata
5 | import inkapplications.shade.scenes.structures.ScenePalette
6 | import inkapplications.shade.serialization.FractionalPercentageSerializer
7 | import inkapplications.shade.structures.ResourceReference
8 | import inkapplications.spondee.scalar.Percentage
9 | import kotlinx.serialization.SerialName
10 | import kotlinx.serialization.Serializable
11 |
12 | /**
13 | * Parameters used for creating a new Scene on the Hue bridge.
14 | */
15 | @Serializable
16 | data class SceneCreateParameters(
17 | /**
18 | * Actions to be applied to each device in the scene's group.
19 | */
20 | val actions: List,
21 |
22 | /**
23 | * User-supplied configuration info for the scene.
24 | */
25 | val metadata: SceneMetadata,
26 |
27 | /**
28 | * The group of lights in the scene.
29 | */
30 | val group: ResourceReference,
31 |
32 | /**
33 | * Group of colors that describe the palette of colors used to be used
34 | * when playing dynamics.
35 | */
36 | val palette: ScenePalette? = null,
37 |
38 | /**
39 | * The speed of the dynamic palette.
40 | */
41 | @Serializable(with = FractionalPercentageSerializer::class)
42 | val speed: Percentage? = null,
43 |
44 | /**
45 | * Indicates whether to automatically start the scene dynamically on
46 | * active recall.
47 | */
48 | @SerialName("auto_dynamic")
49 | val autoDynamic: Boolean? = null,
50 | )
51 |
--------------------------------------------------------------------------------
/discover/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("library")
3 | kotlin("plugin.serialization")
4 | id("ink.publishing")
5 | }
6 |
7 | kotlin {
8 | sourceSets {
9 | val commonMain by getting {
10 | dependencies {
11 | implementation(libs.serialization.json)
12 | api(libs.coroutines.core)
13 | implementation(projects.serialization)
14 | api(projects.structures)
15 |
16 | implementation(libs.ktor.client.core)
17 | implementation(libs.ktor.client.contentnegotiation)
18 | implementation(libs.ktor.serialization.json)
19 | }
20 | }
21 |
22 | val commonTest by getting {
23 | dependencies {
24 | implementation(libs.test.core)
25 | implementation(libs.test.annotations)
26 | }
27 | }
28 |
29 | val jvmMain by getting {
30 | dependencies {
31 | implementation(libs.ktor.client.okhttp)
32 | }
33 | }
34 |
35 | val nativeMain by getting {
36 | dependencies {
37 | implementation(libs.ktor.client.cio)
38 | }
39 | }
40 |
41 | val windowsMain by getting {
42 | dependencies {
43 | implementation(libs.ktor.client.winhttp)
44 | }
45 | }
46 |
47 | val iosMain by getting {
48 | dependencies {
49 | implementation(libs.ktor.client.darwin)
50 | }
51 | }
52 |
53 | val jsMain by getting {
54 | dependencies {
55 | implementation(libs.ktor.client.js)
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/core/api/core.api:
--------------------------------------------------------------------------------
1 | public final class inkapplications/shade/core/JvmExtensionsKt {
2 | public static final fun getEvents (Linkapplications/shade/core/Shade;)Linkapplications/shade/events/Events;
3 | }
4 |
5 | public final class inkapplications/shade/core/Shade {
6 | public fun (Linkapplications/shade/structures/HueConfigurationContainer;Lkimchi/logger/KimchiLogger;)V
7 | public synthetic fun (Linkapplications/shade/structures/HueConfigurationContainer;Lkimchi/logger/KimchiLogger;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
8 | public fun (Ljava/lang/String;Linkapplications/shade/structures/AuthToken;Linkapplications/shade/structures/SecurityStrategy;Lkimchi/logger/KimchiLogger;)V
9 | public synthetic fun (Ljava/lang/String;Linkapplications/shade/structures/AuthToken;Linkapplications/shade/structures/SecurityStrategy;Lkimchi/logger/KimchiLogger;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
10 | public final fun getAuth ()Linkapplications/shade/auth/BridgeAuth;
11 | public final fun getConfiguration ()Linkapplications/shade/structures/HueConfigurationContainer;
12 | public final fun getDevices ()Linkapplications/shade/devices/DeviceControls;
13 | public final fun getGroupedLights ()Linkapplications/shade/groupedlights/GroupedLightControls;
14 | public final fun getLights ()Linkapplications/shade/lights/LightControls;
15 | public final fun getOnlineDiscovery ()Linkapplications/shade/discover/BridgeDiscovery;
16 | public final fun getResources ()Linkapplications/shade/resources/ResourceControls;
17 | public final fun getRooms ()Linkapplications/shade/rooms/RoomControls;
18 | public final fun getScenes ()Linkapplications/shade/scenes/SceneControls;
19 | public final fun getZones ()Linkapplications/shade/zones/ZoneControls;
20 | }
21 |
22 |
--------------------------------------------------------------------------------