diff --git a/proto/overlay-sidecar.proto b/proto/overlay-sidecar.proto index baf47e52..3b3e98e8 100644 --- a/proto/overlay-sidecar.proto +++ b/proto/overlay-sidecar.proto @@ -58,6 +58,7 @@ message OyasumiSidecarState { bool sleep_preparation_available = 9; bool sleep_preparation_timed_out = 10; bool system_mic_muted = 11; + OyasumiSidecarCCTState cct_state = 12; } // No longer required, but reserved for future settings @@ -162,6 +163,15 @@ message OyasumiSidecarBrightnessState { double hardware_max_brightness = 13; } +message OyasumiSidecarCCTState { + bool enabled = 1; + uint32 value = 2; + uint32 min = 3; + uint32 max = 4; + bool transitioning = 5; + uint32 transition_target = 6; +} + enum VrcStatus { VRC_STATUS_Offline = 0; VRC_STATUS_Busy = 1; diff --git a/src-grpc-web-client/overlay-sidecar_pb.ts b/src-grpc-web-client/overlay-sidecar_pb.ts index 5cd76dae..8ff26cb6 100644 --- a/src-grpc-web-client/overlay-sidecar_pb.ts +++ b/src-grpc-web-client/overlay-sidecar_pb.ts @@ -128,6 +128,10 @@ export interface OyasumiSidecarState { * @generated from protobuf field: bool system_mic_muted = 11; */ systemMicMuted: boolean; + /** + * @generated from protobuf field: OyasumiOverlaySidecar.OyasumiSidecarCCTState cct_state = 12; + */ + cctState?: OyasumiSidecarCCTState; } /** * No longer required, but reserved for future settings @@ -434,6 +438,35 @@ export interface OyasumiSidecarBrightnessState { */ hardwareMaxBrightness: number; } +/** + * @generated from protobuf message OyasumiOverlaySidecar.OyasumiSidecarCCTState + */ +export interface OyasumiSidecarCCTState { + /** + * @generated from protobuf field: bool enabled = 1; + */ + enabled: boolean; + /** + * @generated from protobuf field: uint32 value = 2; + */ + value: number; + /** + * @generated from protobuf field: uint32 min = 3; + */ + min: number; + /** + * @generated from protobuf field: uint32 max = 4; + */ + max: number; + /** + * @generated from protobuf field: bool transitioning = 5; + */ + transitioning: boolean; + /** + * @generated from protobuf field: uint32 transition_target = 6; + */ + transitionTarget: number; +} /** * @generated from protobuf enum OyasumiOverlaySidecar.OyasumiSidecarAutomationsState_AutoAcceptInviteRequests_Mode */ @@ -962,6 +995,7 @@ class OyasumiSidecarState$Type extends MessageType { { no: 9, name: 'sleep_preparation_available', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ }, { no: 10, name: 'sleep_preparation_timed_out', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ }, { no: 11, name: 'system_mic_muted', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ }, + { no: 12, name: 'cct_state', kind: 'message', T: () => OyasumiSidecarCCTState }, ]); } create(value?: PartialMessage): OyasumiSidecarState { @@ -1042,6 +1076,14 @@ class OyasumiSidecarState$Type extends MessageType { case /* bool system_mic_muted */ 11: message.systemMicMuted = reader.bool(); break; + case /* OyasumiOverlaySidecar.OyasumiSidecarCCTState cct_state */ 12: + message.cctState = OyasumiSidecarCCTState.internalBinaryRead( + reader, + reader.uint32(), + options, + message.cctState + ); + break; default: let u = options.readUnknownField; if (u === 'throw') @@ -1112,6 +1154,13 @@ class OyasumiSidecarState$Type extends MessageType { /* bool system_mic_muted = 11; */ if (message.systemMicMuted !== false) writer.tag(11, WireType.Varint).bool(message.systemMicMuted); + /* OyasumiOverlaySidecar.OyasumiSidecarCCTState cct_state = 12; */ + if (message.cctState) + OyasumiSidecarCCTState.internalBinaryWrite( + message.cctState, + writer.tag(12, WireType.LengthDelimited).fork(), + options + ).join(); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); return writer; @@ -2414,6 +2463,106 @@ class OyasumiSidecarBrightnessState$Type extends MessageType { + constructor() { + super('OyasumiOverlaySidecar.OyasumiSidecarCCTState', [ + { no: 1, name: 'enabled', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ }, + { no: 2, name: 'value', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ }, + { no: 3, name: 'min', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ }, + { no: 4, name: 'max', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ }, + { no: 5, name: 'transitioning', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ }, + { no: 6, name: 'transition_target', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ }, + ]); + } + create(value?: PartialMessage): OyasumiSidecarCCTState { + const message = { + enabled: false, + value: 0, + min: 0, + max: 0, + transitioning: false, + transitionTarget: 0, + }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: OyasumiSidecarCCTState + ): OyasumiSidecarCCTState { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* bool enabled */ 1: + message.enabled = reader.bool(); + break; + case /* uint32 value */ 2: + message.value = reader.uint32(); + break; + case /* uint32 min */ 3: + message.min = reader.uint32(); + break; + case /* uint32 max */ 4: + message.max = reader.uint32(); + break; + case /* bool transitioning */ 5: + message.transitioning = reader.bool(); + break; + case /* uint32 transition_target */ 6: + message.transitionTarget = reader.uint32(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error( + `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}` + ); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)( + this.typeName, + message, + fieldNo, + wireType, + d + ); + } + } + return message; + } + internalBinaryWrite( + message: OyasumiSidecarCCTState, + writer: IBinaryWriter, + options: BinaryWriteOptions + ): IBinaryWriter { + /* bool enabled = 1; */ + if (message.enabled !== false) writer.tag(1, WireType.Varint).bool(message.enabled); + /* uint32 value = 2; */ + if (message.value !== 0) writer.tag(2, WireType.Varint).uint32(message.value); + /* uint32 min = 3; */ + if (message.min !== 0) writer.tag(3, WireType.Varint).uint32(message.min); + /* uint32 max = 4; */ + if (message.max !== 0) writer.tag(4, WireType.Varint).uint32(message.max); + /* bool transitioning = 5; */ + if (message.transitioning !== false) writer.tag(5, WireType.Varint).bool(message.transitioning); + /* uint32 transition_target = 6; */ + if (message.transitionTarget !== 0) + writer.tag(6, WireType.Varint).uint32(message.transitionTarget); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message OyasumiOverlaySidecar.OyasumiSidecarCCTState + */ +export const OyasumiSidecarCCTState = new OyasumiSidecarCCTState$Type(); /** * @generated ServiceType for protobuf service OyasumiOverlaySidecar.OyasumiOverlaySidecar */ diff --git a/src-overlay-ui/package-lock.json b/src-overlay-ui/package-lock.json index ca3857e6..6efac715 100644 --- a/src-overlay-ui/package-lock.json +++ b/src-overlay-ui/package-lock.json @@ -13,7 +13,8 @@ "just-throttle": "^4.2.0", "lodash-es": "^4.17.21", "material-icons": "^1.13.8", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "tailwindcss-multi": "^0.4.6" }, "devDependencies": { "@sveltejs/adapter-auto": "^2.0.0", @@ -515,6 +516,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/node": { + "version": "20.15.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.15.0.tgz", + "integrity": "sha512-eQf4OkH6gA9v1W0iEpht/neozCsZKMTK+C4cU6/fv7wtJCCL8LEQ4hie2Ln8ZP/0YYM2xGj7//f8xyqItkJ6QA==", + "dependencies": { + "undici-types": "~6.13.0" + } + }, "node_modules/@types/pug": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz", @@ -3485,6 +3494,14 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-multi": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/tailwindcss-multi/-/tailwindcss-multi-0.4.6.tgz", + "integrity": "sha512-Bn9yLrMkeYFrJjRYuuTfqT9ibm6U2qnTwlIKYchWyGX3qgL+1+NN4JgiHJG1qrR4QQeU28eCl8sMSBobdzijvQ==", + "dependencies": { + "@types/node": "^20.4.1" + } + }, "node_modules/tailwindcss/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3685,6 +3702,11 @@ "node": ">=14.0" } }, + "node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -4290,6 +4312,14 @@ "@types/lodash": "*" } }, + "@types/node": { + "version": "20.15.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.15.0.tgz", + "integrity": "sha512-eQf4OkH6gA9v1W0iEpht/neozCsZKMTK+C4cU6/fv7wtJCCL8LEQ4hie2Ln8ZP/0YYM2xGj7//f8xyqItkJ6QA==", + "requires": { + "undici-types": "~6.13.0" + } + }, "@types/pug": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz", @@ -6221,6 +6251,14 @@ } } }, + "tailwindcss-multi": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/tailwindcss-multi/-/tailwindcss-multi-0.4.6.tgz", + "integrity": "sha512-Bn9yLrMkeYFrJjRYuuTfqT9ibm6U2qnTwlIKYchWyGX3qgL+1+NN4JgiHJG1qrR4QQeU28eCl8sMSBobdzijvQ==", + "requires": { + "@types/node": "^20.4.1" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6328,6 +6366,11 @@ "busboy": "^1.6.0" } }, + "undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + }, "update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", diff --git a/src-overlay-ui/package.json b/src-overlay-ui/package.json index 89814f56..07b30494 100644 --- a/src-overlay-ui/package.json +++ b/src-overlay-ui/package.json @@ -42,6 +42,7 @@ "just-throttle": "^4.2.0", "lodash-es": "^4.17.21", "material-icons": "^1.13.8", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "tailwindcss-multi": "^0.4.6" } -} \ No newline at end of file +} diff --git a/src-overlay-ui/splash/index.html b/src-overlay-ui/splash/index.html new file mode 100644 index 00000000..5c42dedf --- /dev/null +++ b/src-overlay-ui/splash/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + +
+ +
+ + diff --git a/src-overlay-ui/src/lib/components/BrightnessSlider.svelte b/src-overlay-ui/src/lib/components/BrightnessSlider.svelte index 479094c5..04020a0d 100644 --- a/src-overlay-ui/src/lib/components/BrightnessSlider.svelte +++ b/src-overlay-ui/src/lib/components/BrightnessSlider.svelte @@ -130,10 +130,6 @@ &-title-row { @apply flex flex-row items-center mb-4; - .material-icons { - @apply text-2xl text-white text-center inline-block; - flex-shrink: 0; - } span { @apply text-2xl text-white text-center inline-block; @@ -208,9 +204,7 @@
- {label} -
diff --git a/src-overlay-ui/src/lib/components/BrightnessSliders.svelte b/src-overlay-ui/src/lib/components/BrightnessSliders.svelte index 9f237d10..32079992 100644 --- a/src-overlay-ui/src/lib/components/BrightnessSliders.svelte +++ b/src-overlay-ui/src/lib/components/BrightnessSliders.svelte @@ -1,71 +1,83 @@ +
+ {#if !!brightnessState} + {#if !brightnessState?.advancedMode} +
+ ipc.setBrightness('SIMPLE', value), 16, { + leading: true, + trailing: true + })} + /> +
+ {:else} +
+ ipc.setBrightness('SOFTWARE', value), 16, { + leading: true, + trailing: true + })} + /> + ipc.setBrightness('HARDWARE', value), 16, { + leading: true, + trailing: true + })} + /> +
+ {/if} + {/if} +
+ - -
- {#if !!brightnessState} - {#if !brightnessState?.advancedMode} -
- ipc.setBrightness('SIMPLE', value), 16, {leading: true, trailing: true})} - > -
- {:else} -
- ipc.setBrightness('SOFTWARE', value), 16, {leading: true, trailing: true})} - > - ipc.setBrightness('HARDWARE', value), 16, {leading: true, trailing: true})} - > -
- {/if} - {/if} -
diff --git a/src-overlay-ui/src/lib/components/ColorTempSlider.svelte b/src-overlay-ui/src/lib/components/ColorTempSlider.svelte new file mode 100644 index 00000000..e959f386 --- /dev/null +++ b/src-overlay-ui/src/lib/components/ColorTempSlider.svelte @@ -0,0 +1,232 @@ + + +
+
+
+ {#if !dragging} + {label} + {:else} + {renderValue} + {/if} +
+
+
+
+
+
+
+
+ + diff --git a/src-overlay-ui/src/lib/models/OyasumiState.ts b/src-overlay-ui/src/lib/models/OyasumiState.ts index 52cbd4fe..b921c9d5 100644 --- a/src-overlay-ui/src/lib/models/OyasumiState.ts +++ b/src-overlay-ui/src/lib/models/OyasumiState.ts @@ -56,6 +56,14 @@ export const DEFAULT_OYASUMI_STATE: OyasumiSidecarState = { hardwareMinBrightness: 20, hardwareMaxBrightness: 160 }, + cctState: { + enabled: true, + value: 6600, + min: 1000, + max: 10000, + transitioning: false, + transitionTarget: 6600 + }, sleepPreparationAvailable: false, sleepPreparationTimedOut: false, systemMicMuted: false diff --git a/src-overlay-ui/src/lib/services/ipc.service.ts b/src-overlay-ui/src/lib/services/ipc.service.ts index 457a2340..e2f30636 100644 --- a/src-overlay-ui/src/lib/services/ipc.service.ts +++ b/src-overlay-ui/src/lib/services/ipc.service.ts @@ -168,6 +168,15 @@ class IPCService { }); } + public async setColorTemperature(value: number): Promise { + this.state.update((state) => { + state = cloneDeep(state); + state.cctState!.value = value; + window.OyasumiIPCOut.sendEventDouble('setColorTemperature', value); + return state; + }); + } + public async prepareForSleep() { await window.OyasumiIPCOut.sendEventVoid('prepareForSleep'); } diff --git a/src-overlay-ui/src/routes/dashboard/Overview.svelte b/src-overlay-ui/src/routes/dashboard/Overview.svelte index 8cec6a0e..d52aaf46 100644 --- a/src-overlay-ui/src/routes/dashboard/Overview.svelte +++ b/src-overlay-ui/src/routes/dashboard/Overview.svelte @@ -1,254 +1,299 @@ -
- -
- Oyasumi Logo -
-
- -
- - -
- nights_stay -
- {$t('t.overlay.dashboard.overview.sleepMode')} - {$state.sleepMode - ? $t('t.overlay.dashboard.overview.active') - : $t('t.overlay.dashboard.overview.inactive')} -
-
-
-
-
- -
- { if (sleepPreparationEnabled) ipc.prepareForSleep() }} - tooltip={$t('t.overlay.dashboard.overview.tooltip.prepareForSleep')} - > - -
- bedtime -
-
-
-
- -
+ +
+ Oyasumi Logo +
+
+ +
+ + +
+ nights_stay +
+ {$t('t.overlay.dashboard.overview.sleepMode')} + {$state.sleepMode + ? $t('t.overlay.dashboard.overview.active') + : $t('t.overlay.dashboard.overview.inactive')} +
+
+
+
+
+ +
+ { + if (sleepPreparationEnabled) ipc.prepareForSleep(); + }} + tooltip={$t('t.overlay.dashboard.overview.tooltip.prepareForSleep')} + > + +
+ bedtime +
+
+
+
+ +
- { + > + { dispatch('nav', { mode: 'AUTOMATIONS' }); }} - tooltip={$t('t.overlay.dashboard.overview.tooltip.automations')} - > - -
- settings_suggest -
-
-
-
- -
+ +
+ settings_suggest +
+
+ +
+ +
- { + > + { dispatch('nav', { mode: 'DEVICE_CONTROL' }); }} - tooltip={$t('t.overlay.dashboard.overview.tooltip.deviceControl')} - > - -
- -
-
-
-
- -
+ +
+ +
+
+ +
+ +
- { + > + { if (!shutdownSequenceDisabled) dispatch('openShutdownSequence'); }} - tooltip={$t('t.overlay.dashboard.overview.tooltip.shutdown')} - > - -
- settings_power -
-
-
-
- -
- -
+ +
+ settings_power +
+
+ +
+
+ +
- -
- {#if $vrcLoggedIn} -
-
+ {#if $vrcLoggedIn} +
+
- {$state.vrcUsername} -
- {/if} -
- {timeHours}:{timeMinutes} - access_time -
-
- -
- -
+ {$state.vrcUsername} +
+ {/if} +
+ {timeHours}:{timeMinutes} + access_time +
+
+ + {#if cctState.enabled} + sliderMode.set($sliderModeButtonMode)} + > + + {#if $sliderModeButtonMode === 'COLOR_TEMP'} +
+ thermostat +
+ {:else if $sliderModeButtonMode === 'BRIGHTNESS'} +
+ wb_sunny +
+ {/if} +
+
+ {/if} + {/if} +
+ +
- -
+ > + {#if $sliderMode === 'BRIGHTNESS'} +
+ +
+ {:else if $sliderMode === 'COLOR_TEMP'} +
+ {#if !!cctState} + ipc.setColorTemperature(value), 16, { + leading: true, + trailing: true + })} + /> + {/if} +
+ {/if} +
diff --git a/src-overlay-ui/tailwind.config.js b/src-overlay-ui/tailwind.config.js index 7aa38ee2..34cb3b6f 100644 --- a/src-overlay-ui/tailwind.config.js +++ b/src-overlay-ui/tailwind.config.js @@ -1,10 +1,9 @@ /** @type {import("tailwindcss").Config} */ export default { - mode: "jit", - purge: ["./src/**/*.svelte"], - theme: { - extend: {} - }, - plugins: [] + mode: 'jit', + purge: ['./src/**/*.svelte'], + theme: { + extend: {} + }, + plugins: [require('tailwindcss-multi')] }; - diff --git a/src-ui/assets/i18n/en.json b/src-ui/assets/i18n/en.json index c2ddad79..0c1d8d3e 100644 --- a/src-ui/assets/i18n/en.json +++ b/src-ui/assets/i18n/en.json @@ -1293,6 +1293,7 @@ "simple": "Brightness", "software": "Software Brightness" }, + "colorTemp": "Color Temperature", "dashboard": { "automations": { "autoAcceptInviteRequests": { @@ -1340,7 +1341,11 @@ "automations": "Automations", "deviceControl": "Device Control", "prepareForSleep": "Prepare for sleep", - "shutdown": "Shutdown Sequence" + "shutdown": "Shutdown Sequence", + "sliderMode": { + "BRIGHTNESS": "Brightness Control", + "COLOR_TEMP": "Color Temperature Control" + } } }, "shutdownSequence": { @@ -2505,4 +2510,4 @@ }, "title": "VRChat Microphone Mute Automations" } -} \ No newline at end of file +}