diff --git a/hikvision-doorbell/DOCS.md b/hikvision-doorbell/DOCS.md index 8dfbdea..7bac16b 100644 --- a/hikvision-doorbell/DOCS.md +++ b/hikvision-doorbell/DOCS.md @@ -154,24 +154,26 @@ The input string must be in the format ``` - `` is one of: - | Command | Description | - | -------- | ---- | - | unlock | Unlock the specified door (`` must be `1` or `2`) connected to the doorbell station output relay - | reboot | Reboot the specified door station - | reject | Reject the incoming call and stop the indoor stations from ringing - | request | Unknown - | cancel | Unknown - | answer | Unknown - | reject | Unknown - | bellTimeout | Unknown - | hangUp | Unknown - | deviceOnCall| Unknown - | atHome | Sending scene "At home" for indoor panels - | goOut | Sending scene "Go out" for indoor panels - | goToBed | Sending scene "Go to bed" for indoor panels - | custom | Sending scene "custom" for indoor panels - | setupAlarm | Turn on the alarm on the indoor panel - | closeAlarm | Turn off the alarm on the indoor panel + | Command | Description | + | -------- | ---- | + | unlock | Unlock the specified door (`` must be `1` or `2`) connected to the doorbell station output relay + | reboot | Reboot the specified door station + | reject | Reject the incoming call and stop the indoor stations from ringing + | request | Unknown + | cancel | Unknown + | answer | Unknown + | reject | Unknown + | bellTimeout | Unknown + | hangUp | Unknown + | deviceOnCall | Unknown + | atHome | Sending scene "At home" for indoor panels + | goOut | Sending scene "Go out" for indoor panels + | goToBed | Sending scene "Go to bed" for indoor panels + | custom | Sending scene "custom" for indoor panels + | setupAlarm | Turn on the alarm on the indoor panel + | closeAlarm | Turn off the alarm on the indoor panel + | muteAudioOutput | Mutes the audio output of the doorbell / indoor station + | unmuteAudioOutput | Unmutes the audio output of the doorbell / indoor station - `` is the custom name given to the doorbell in the configuration options, all lowercase and with whitespace substituted by underscores `_`. E.G.: If the doorbell is named `Front door`, the input string must reference it as `front_door`. diff --git a/hikvision-doorbell/src/doorbell.py b/hikvision-doorbell/src/doorbell.py index ab9333f..b1e4913 100644 --- a/hikvision-doorbell/src/doorbell.py +++ b/hikvision-doorbell/src/doorbell.py @@ -37,6 +37,8 @@ class Doorbell(): _type: DeviceType _device_info: NET_DVR_DEVICEINFO_V30 '''Populated after authenticate method is invoked''' + _previouse_audio_out_volume: str + '''Used to unmute the doorbell by changing the audio out volume from 0 to the previouse value ''' def __init__(self, id: int, config: AppConfig.Doorbell, sdk: CDLL): """ @@ -47,6 +49,7 @@ def __init__(self, id: int, config: AppConfig.Doorbell, sdk: CDLL): self._sdk = sdk self._config = config self._id = id + self._previouse_audio_out_volume = "5" def authenticate(self): '''Authenticate with the remote doorbell''' @@ -355,6 +358,63 @@ def get_device_info(self): xml_string = self._call_isapi("GET", "/ISAPI/System/deviceInfo") return ET.fromstring(xml_string) + def get_audio_out_settings(self): + """Retrieve audio output seetings of channel 1 (volume of the output and talk volume) using the ISAPI endpoint. + Return the parsed XML document""" + xml_string = self._call_isapi("GET", "/ISAPI/System/Audio/AudioOut/channels/1") + return ET.fromstring(xml_string) + + def mute_audio_output(self): + try: + current_settings = self.get_audio_out_settings() + + currentTalkVolume = current_settings.find('{*}talkVolume') + if currentTalkVolume is None or currentTalkVolume.text is None: + talkVolume = "5" + else: + talkVolume = currentTalkVolume.text + + currentVolume = current_settings.find('{*}volume') + if currentVolume is None or currentVolume.text is None: + self._previouse_audio_out_volume = 5 + else: + # remember current audio out volume for the unmute of the doorbell + self._previouse_audio_out_volume = int(currentVolume.text) + + except SDKError: + # Cannot get current audio out settings use default values + talkVolume = "5" + self._previouse_audio_out_volume = "5" + + url = "/ISAPI/System/Audio/AudioOut/channels/1" + # mute audio out by changing the audio out volume to 0 + requestBody = """1audioOutput + 0{} + """.format(talkVolume) + + self._call_isapi("PUT", url, requestBody) + + def unmute_audio_output(self): + try: + current_settings = self.get_audio_out_settings() + currentTalkVolume = current_settings.find('{*}talkVolume') + if currentTalkVolume is None or currentTalkVolume.text is None: + talkVolume = "5" + else: + talkVolume = currentTalkVolume.text + except SDKError: + # Cannot get current audio out settings use default values + talkVolume = "5" + + url = "/ISAPI/System/Audio/AudioOut/channels/1" + + # unmute audio out by changing the audio out volume back to the previouse volume + requestBody = """1audioOutput + {}{} + """.format(self._previouse_audio_out_volume, talkVolume) + + self._call_isapi("PUT", url, requestBody) + def get_call_status(self) -> int: """Get the current status of the call.""" call_status = NET_DVR_CALL_STATUS() diff --git a/hikvision-doorbell/src/input.py b/hikvision-doorbell/src/input.py index 40fb17d..e6fe612 100644 --- a/hikvision-doorbell/src/input.py +++ b/hikvision-doorbell/src/input.py @@ -149,6 +149,12 @@ def execute_command(self, command: str): case "reboot": logger.info("Rebooting door station") doorbell.reboot_device() + case "muteAudioOutput": + logger.info("Mute audio output") + doorbell.mute_audio_output() + case "unmuteAudioOutput": + logger.info("Unmute audio output") + doorbell.unmute_audio_output() case "debug": # This is a special command that accept the name of a method, # calls the method on the doorbell instance and outputs the result diff --git a/hikvision-doorbell/src/mqtt_input.py b/hikvision-doorbell/src/mqtt_input.py index dd3f8eb..56d6da7 100644 --- a/hikvision-doorbell/src/mqtt_input.py +++ b/hikvision-doorbell/src/mqtt_input.py @@ -87,6 +87,30 @@ def __init__(self, config: AppConfig.MQTT, doorbells: Registry) -> None: answer_button = Button(settings, self._answer_call_callback, doorbell) answer_button.set_availability(True) + ########### + # Mute audio output button + button_info = ButtonInfo( + name="Mute audio output", + unique_id=f"{sanitized_doorbell_name}_mute_audio_output", + device=device, + icon="mdi:volume-mute", + object_id=f"{sanitized_doorbell_name}_mute_audio_output") + settings = Settings(mqtt=mqtt_settings, entity=button_info, manual_availability=True) + mute_button = Button(settings, self._mute_audio_output_callback, doorbell) + mute_button.set_availability(True) + + ########### + # Unmute audio output button + button_info = ButtonInfo( + name="Unmute audio output", + unique_id=f"{sanitized_doorbell_name}_unmute_audio_output", + device=device, + icon="mdi:volume-high", + object_id=f"{sanitized_doorbell_name}_unmute_audio_output") + settings = Settings(mqtt=mqtt_settings, entity=button_info, manual_availability=True) + unmute_button = Button(settings, self._unmute_audio_output_callback, doorbell) + unmute_button.set_availability(True) + ########### # ISAPI request input text text_info = TextInfo( @@ -412,6 +436,22 @@ def _closeAlarm_callback(self, client, doorbell: Doorbell, message: MQTTMessage) except SDKError as err: logger.error("Error setting scene: {}", err) + def _mute_audio_output_callback(self, client, doorbell: Doorbell, message: MQTTMessage): + logger.info("Received mute audio output command for doorbell: {}", doorbell._config.name) + + try: + doorbell.mute_audio_output() + except SDKError as err: + logger.error("Error setting scene: {}", err) + + def _unmute_audio_output_callback(self, client, doorbell: Doorbell, message: MQTTMessage): + logger.info("Received unmute audio output command for doorbell: {}", doorbell._config.name) + + try: + doorbell.unmute_audio_output() + except SDKError as err: + logger.error("Error setting scene: {}", err) + def _isapi_input_callback(self, client, doorbell: Doorbell, message: MQTTMessage): logger.debug("Received input text for doorbell: {}", doorbell._config.name)