From 154e2296cfa73496d4433c2b4190f67571c1a3fb Mon Sep 17 00:00:00 2001 From: Jeff Cooper Date: Sun, 10 Nov 2024 01:58:00 -0500 Subject: [PATCH] Made mic mute switch functional --- .../hassmic/proto/ServerMessage.java | 252 +++++++++++++++++- .../hassmic/proto/ServerMessageOrBuilder.java | 38 +++ app/app/cheyenne.ts | 13 +- app/app/proto/hassmic.ts | 22 +- custom_components/hassmic/proto/hassmic.py | 3 + .../hassmic/switch/microphone.py | 11 +- proto/hassmic.proto | 4 + 7 files changed, 325 insertions(+), 18 deletions(-) diff --git a/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessage.java b/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessage.java index 02c132b..8f51334 100644 --- a/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessage.java +++ b/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessage.java @@ -24,6 +24,7 @@ private ServerMessage() {} public enum MsgCase { PLAY_AUDIO(1), + SET_MIC_MUTE(2), MSG_NOT_SET(0); private final int value; @@ -43,6 +44,8 @@ public static MsgCase forNumber(int value) { switch (value) { case 1: return PLAY_AUDIO; + case 2: + return SET_MIC_MUTE; case 0: return MSG_NOT_SET; default: @@ -67,13 +70,29 @@ private void clearMsg() { public static final int PLAY_AUDIO_FIELD_NUMBER = 1; - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+   * A command to play audio
+   * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ @java.lang.Override public boolean hasPlayAudio() { return msgCase_ == 1; } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+   * A command to play audio
+   * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ @java.lang.Override public com.thejeffcooper.hassmic.proto.PlayAudio getPlayAudio() { if (msgCase_ == 1) { @@ -82,14 +101,30 @@ public com.thejeffcooper.hassmic.proto.PlayAudio getPlayAudio() { return com.thejeffcooper.hassmic.proto.PlayAudio.getDefaultInstance(); } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+   * A command to play audio
+   * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ private void setPlayAudio(com.thejeffcooper.hassmic.proto.PlayAudio value) { value.getClass(); msg_ = value; msgCase_ = 1; } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+   * A command to play audio
+   * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ private void mergePlayAudio(com.thejeffcooper.hassmic.proto.PlayAudio value) { value.getClass(); if (msgCase_ == 1 && msg_ != com.thejeffcooper.hassmic.proto.PlayAudio.getDefaultInstance()) { @@ -104,7 +139,15 @@ private void mergePlayAudio(com.thejeffcooper.hassmic.proto.PlayAudio value) { msgCase_ = 1; } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+   * A command to play audio
+   * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ private void clearPlayAudio() { if (msgCase_ == 1) { msgCase_ = 0; @@ -112,6 +155,75 @@ private void clearPlayAudio() { } } + public static final int SET_MIC_MUTE_FIELD_NUMBER = 2; + + /** + * + * + *
+   * Set whether the mic should be muted
+   * 
+ * + * bool set_mic_mute = 2; + * + * @return Whether the setMicMute field is set. + */ + @java.lang.Override + public boolean hasSetMicMute() { + return msgCase_ == 2; + } + + /** + * + * + *
+   * Set whether the mic should be muted
+   * 
+ * + * bool set_mic_mute = 2; + * + * @return The setMicMute. + */ + @java.lang.Override + public boolean getSetMicMute() { + if (msgCase_ == 2) { + return (java.lang.Boolean) msg_; + } + return false; + } + + /** + * + * + *
+   * Set whether the mic should be muted
+   * 
+ * + * bool set_mic_mute = 2; + * + * @param value The setMicMute to set. + */ + private void setSetMicMute(boolean value) { + msgCase_ = 2; + msg_ = value; + } + + /** + * + * + *
+   * Set whether the mic should be muted
+   * 
+ * + * bool set_mic_mute = 2; + */ + private void clearSetMicMute() { + if (msgCase_ == 2) { + msgCase_ = 0; + msg_ = null; + } + } + public static com.thejeffcooper.hassmic.proto.ServerMessage parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return com.google.protobuf.GeneratedMessageLite.parseFrom(DEFAULT_INSTANCE, data); @@ -225,46 +337,161 @@ public Builder clearMsg() { return this; } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+     * A command to play audio
+     * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ @java.lang.Override public boolean hasPlayAudio() { return instance.hasPlayAudio(); } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+     * A command to play audio
+     * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ @java.lang.Override public com.thejeffcooper.hassmic.proto.PlayAudio getPlayAudio() { return instance.getPlayAudio(); } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+     * A command to play audio
+     * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ public Builder setPlayAudio(com.thejeffcooper.hassmic.proto.PlayAudio value) { copyOnWrite(); instance.setPlayAudio(value); return this; } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+     * A command to play audio
+     * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ public Builder setPlayAudio(com.thejeffcooper.hassmic.proto.PlayAudio.Builder builderForValue) { copyOnWrite(); instance.setPlayAudio(builderForValue.build()); return this; } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+     * A command to play audio
+     * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ public Builder mergePlayAudio(com.thejeffcooper.hassmic.proto.PlayAudio value) { copyOnWrite(); instance.mergePlayAudio(value); return this; } - /** .hassmic.PlayAudio play_audio = 1; */ + /** + * + * + *
+     * A command to play audio
+     * 
+ * + * .hassmic.PlayAudio play_audio = 1; + */ public Builder clearPlayAudio() { copyOnWrite(); instance.clearPlayAudio(); return this; } + /** + * + * + *
+     * Set whether the mic should be muted
+     * 
+ * + * bool set_mic_mute = 2; + * + * @return Whether the setMicMute field is set. + */ + @java.lang.Override + public boolean hasSetMicMute() { + return instance.hasSetMicMute(); + } + + /** + * + * + *
+     * Set whether the mic should be muted
+     * 
+ * + * bool set_mic_mute = 2; + * + * @return The setMicMute. + */ + @java.lang.Override + public boolean getSetMicMute() { + return instance.getSetMicMute(); + } + + /** + * + * + *
+     * Set whether the mic should be muted
+     * 
+ * + * bool set_mic_mute = 2; + * + * @param value The setMicMute to set. + * @return This builder for chaining. + */ + public Builder setSetMicMute(boolean value) { + copyOnWrite(); + instance.setSetMicMute(value); + return this; + } + + /** + * + * + *
+     * Set whether the mic should be muted
+     * 
+ * + * bool set_mic_mute = 2; + * + * @return This builder for chaining. + */ + public Builder clearSetMicMute() { + copyOnWrite(); + instance.clearSetMicMute(); + return this; + } + // @@protoc_insertion_point(builder_scope:hassmic.ServerMessage) } @@ -290,7 +517,8 @@ protected final java.lang.Object dynamicMethod( "msg_", "msgCase_", com.thejeffcooper.hassmic.proto.PlayAudio.class, }; java.lang.String info = - "\u0000\u0001\u0001\u0000\u0001\u0001\u0001\u0000\u0000\u0000\u0001<\u0000"; + "\u0000\u0002\u0001\u0000\u0001\u0002\u0002\u0000\u0000\u0000\u0001<\u0000\u0002:" + + "\u0000"; return newMessageInfo(DEFAULT_INSTANCE, info, objects); } // fall through diff --git a/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessageOrBuilder.java b/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessageOrBuilder.java index 51c9048..923d22f 100644 --- a/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessageOrBuilder.java +++ b/app/android/app/src/main/java/com/thejeffcooper/hassmic/proto/ServerMessageOrBuilder.java @@ -9,6 +9,12 @@ public interface ServerMessageOrBuilder com.google.protobuf.MessageLiteOrBuilder { /** + * + * + *
+   * A command to play audio
+   * 
+ * * .hassmic.PlayAudio play_audio = 1; * * @return Whether the playAudio field is set. @@ -16,11 +22,43 @@ public interface ServerMessageOrBuilder boolean hasPlayAudio(); /** + * + * + *
+   * A command to play audio
+   * 
+ * * .hassmic.PlayAudio play_audio = 1; * * @return The playAudio. */ com.thejeffcooper.hassmic.proto.PlayAudio getPlayAudio(); + /** + * + * + *
+   * Set whether the mic should be muted
+   * 
+ * + * bool set_mic_mute = 2; + * + * @return Whether the setMicMute field is set. + */ + boolean hasSetMicMute(); + + /** + * + * + *
+   * Set whether the mic should be muted
+   * 
+ * + * bool set_mic_mute = 2; + * + * @return The setMicMute. + */ + boolean getSetMicMute(); + public com.thejeffcooper.hassmic.proto.ServerMessage.MsgCase getMsgCase(); } diff --git a/app/app/cheyenne.ts b/app/app/cheyenne.ts index 58b5b0e..ad9c049 100644 --- a/app/app/cheyenne.ts +++ b/app/app/cheyenne.ts @@ -29,6 +29,9 @@ class CheyenneServer { // settable callback for connection state private _connectionStateCallback: CallbackType = null; + // Whether the mic should be muted + private _mic_muted: boolean = false; + setConnectionStateCallback = (cb: CallbackType) => { this._connectionStateCallback = cb; }; @@ -45,7 +48,7 @@ class CheyenneServer { }; streamAudio = (streamData: Uint8Array) => { - if (this._sock) { + if (this._sock && !this._mic_muted) { try { this.sendMessage( ClientMessage.create({ @@ -184,8 +187,14 @@ class CheyenneServer { } } break; + case "setMicMute": + console.info("Got set_mic_mute message"); + const shouldMute: boolean = m.msg.setMicMute; + console.info(`Setting mic mute to ${shouldMute}`); + this._mic_muted = shouldMute; + break; default: - console.warn(`Got unknown message type '${d.msg.oneofKind}'`); + console.warn(`Got unknown message type '${m.msg.oneofKind}'`); } } catch (e) { console.error(e); diff --git a/app/app/proto/hassmic.ts b/app/app/proto/hassmic.ts index 561b4d2..b511752 100644 --- a/app/app/proto/hassmic.ts +++ b/app/app/proto/hassmic.ts @@ -183,9 +183,19 @@ export interface ServerMessage { msg: { oneofKind: "playAudio"; /** + * A command to play audio + * * @generated from protobuf field: hassmic.PlayAudio play_audio = 1; */ playAudio: PlayAudio; + } | { + oneofKind: "setMicMute"; + /** + * Set whether the mic should be muted + * + * @generated from protobuf field: bool set_mic_mute = 2; + */ + setMicMute: boolean; } | { oneofKind: undefined; }; @@ -733,7 +743,8 @@ export const PlayAudio = new PlayAudio$Type(); class ServerMessage$Type extends MessageType { constructor() { super("hassmic.ServerMessage", [ - { no: 1, name: "play_audio", kind: "message", oneof: "msg", T: () => PlayAudio } + { no: 1, name: "play_audio", kind: "message", oneof: "msg", T: () => PlayAudio }, + { no: 2, name: "set_mic_mute", kind: "scalar", oneof: "msg", T: 8 /*ScalarType.BOOL*/ } ]); } create(value?: PartialMessage): ServerMessage { @@ -754,6 +765,12 @@ class ServerMessage$Type extends MessageType { playAudio: PlayAudio.internalBinaryRead(reader, reader.uint32(), options, (message.msg as any).playAudio) }; break; + case /* bool set_mic_mute */ 2: + message.msg = { + oneofKind: "setMicMute", + setMicMute: reader.bool() + }; + break; default: let u = options.readUnknownField; if (u === "throw") @@ -769,6 +786,9 @@ class ServerMessage$Type extends MessageType { /* hassmic.PlayAudio play_audio = 1; */ if (message.msg.oneofKind === "playAudio") PlayAudio.internalBinaryWrite(message.msg.playAudio, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + /* bool set_mic_mute = 2; */ + if (message.msg.oneofKind === "setMicMute") + writer.tag(2, WireType.Varint).bool(message.msg.setMicMute); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); diff --git a/custom_components/hassmic/proto/hassmic.py b/custom_components/hassmic/proto/hassmic.py index d5a0ca6..c49474b 100644 --- a/custom_components/hassmic/proto/hassmic.py +++ b/custom_components/hassmic/proto/hassmic.py @@ -111,4 +111,7 @@ class PlayAudio(betterproto.Message): class ServerMessage(betterproto.Message): """The wrapper message that gets sent from the server to the client""" + # A command to play audio play_audio: "PlayAudio" = betterproto.message_field(1, group="msg") + # Set whether the mic should be muted + set_mic_mute: bool = betterproto.bool_field(2, group="msg") diff --git a/custom_components/hassmic/switch/microphone.py b/custom_components/hassmic/switch/microphone.py index e15a80b..a7d8619 100644 --- a/custom_components/hassmic/switch/microphone.py +++ b/custom_components/hassmic/switch/microphone.py @@ -5,6 +5,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from ..proto.hassmic import * from . import base _LOGGER = logging.getLogger(__name__) @@ -25,14 +26,18 @@ def icon(self): def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: super().__init__(hass, config_entry) - self.name = "Microphone (DOES NOTHING!)" + self.name = "Microphone" + + self._hassmic = config_entry.runtime_data def turn_on(self, **kwargs) -> None: - _LOGGER.debug("Would send signal to turn on microphone") + _LOGGER.debug("Sending signal to turn on microphone") + self._hassmic.connection_manager.send_enqueue(ServerMessage(set_mic_mute=False)) self._attr_is_on = True self.schedule_update_ha_state() def turn_off(self, **kwargs) -> None: - _LOGGER.debug("Would send signal to turn off microphone") + _LOGGER.debug("Sending signal to turn off microphone") + self._hassmic.connection_manager.send_enqueue(ServerMessage(set_mic_mute=True)) self._attr_is_on = False self.schedule_update_ha_state() diff --git a/proto/hassmic.proto b/proto/hassmic.proto index f6ab1c0..59ae8b2 100644 --- a/proto/hassmic.proto +++ b/proto/hassmic.proto @@ -91,6 +91,10 @@ message PlayAudio { // The wrapper message that gets sent from the server to the client message ServerMessage { oneof msg { + // A command to play audio PlayAudio play_audio = 1; + + // Set whether the mic should be muted + bool set_mic_mute = 2; } }