Skip to content

Commit

Permalink
Add mute and unmute audio output command / button
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan Babl committed Sep 30, 2023
1 parent 80de2d8 commit c81e865
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 18 deletions.
38 changes: 20 additions & 18 deletions hikvision-doorbell/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,24 +154,26 @@ The input string must be in the format
```
- `<command>` is one of:
| Command | Description |
| -------- | ---- |
| unlock | Unlock the specified door (`<optional_parameter>` 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 (`<optional_parameter>` 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
- `<doorbell_name>` 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`.
Expand Down
60 changes: 60 additions & 0 deletions hikvision-doorbell/src/doorbell.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand All @@ -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'''
Expand Down Expand Up @@ -353,6 +356,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 = """<AudioOut><id>1</id><AudioOutVolumelist><AudioOutVlome><type>audioOutput</type>
<volume>0</volume><talkVolume>{}</talkVolume>
</AudioOutVlome></AudioOutVolumelist></AudioOut>""".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 = """<AudioOut><id>1</id><AudioOutVolumelist><AudioOutVlome><type>audioOutput</type>
<volume>{}</volume><talkVolume>{}</talkVolume>
</AudioOutVlome></AudioOutVolumelist></AudioOut>""".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()
Expand Down
6 changes: 6 additions & 0 deletions hikvision-doorbell/src/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions hikvision-doorbell/src/mqtt_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit c81e865

Please sign in to comment.