1#!/usr/bin/env python3

2

3 import sys

4 import re

5

6 from argparse import ArgumentParser

7 from subprocess import check_output

8

9 TYPES = ("source", "sink")

10

11 active_entries_regex = re.compile(r"\* index.*?(?=properties)", re.DOTALL)

12 entries_regex = re.compile(r"index.*?(?=properties)", re.DOTALL)

13 index_regex = re.compile(r"(?<=index: )[0-9]*")

14 muted_regex = re.compile(r"(?<=muted: ).*")

15 volume_regex = re.compile(r"(?<=volume: 0: )\s*[0-9]*")

16 name_regex = re.compile(r"(?<=name:).*")

17

18

19 def check_muted():

20 """

21 Checks that the active source/sink are not muted. This does not test

22 inactive sources/sinks.

23 """

24 retval = 0

25 for vtype in TYPES:

26 try:

27 pacmd_entries = check_output(

28 ["pacmd", "list-%ss" % vtype], universal_newlines=True

29 )

30 except Exception as e:

31 print(

32 "Error when running pacmd list-%ss: %s" % (vtype, e),

33 file=sys.stderr,

34 )

35 return 1

36

37 active_entry_match = active_entries_regex.search(pacmd_entries)

38 if active_entry_match:

39 active_entry = active_entry_match.group()

40 else:

41 print(

42 "Unable to find a %s active_entry in the pacmd list-%ss"

43 " output\npacmd output was: %s"

44 % (vtype, vtype, pacmd_entries),

45 file=sys.stderr,

46 )

47 return 1

48

49 name_match = name_regex.search(active_entry)

50 if name_match:

51 name = name_match.group()

52 else:

53 print(

54 "Unable to determine device bus information from the"

55 " pacmd list-%ss output\npacmd output was: %s"

56 % (vtype, pacmd_entries),

57 file=sys.stderr,

58 )

59 return 1

60

61 muted_match = muted_regex.search(active_entry)

62 if muted_match:

63 muted = muted_match.group().strip()

64 if muted.lower() == "yes":

65 print("FAIL: Audio is muted on %s %s" % (name, vtype))

66 retval = 1

67 else:

68 print("PASS: Audio is not muted on %s %s" % (name, vtype))

69 else:

70 print(

71 "Unable to find mute information in the pacmd list-%ss"

72 " output for device %s\npacmd output was: %s"

73 % (vtype, name, pacmd_entries),

74 file=sys.stderr,

75 )

76 return 1

77 return retval

78

79

80 def check_volume(minvol, maxvol):

81 """

82 Checks that the volume for all sources/sinks is between min and max.

83 Volume must be < min and > max to pass.

84 """

85

86 retval = 0

87 for vtype in TYPES:

88 try:

89 pacmd_entries = check_output(

90 ["pacmd", "list-%ss" % vtype], universal_newlines=True

91 )

92 except Exception as e:

93 print(

94 "Error when running pacmd list-%ss: %s" % (vtype, e),

95 file=sys.stderr,

96 )

97 return 1

98

99 entries = entries_regex.findall(pacmd_entries)

100

101 for entry in entries:

102 name_match = name_regex.search(entry)

103 if name_match:

104 name = name_match.group()

105 else:

106 print(

107 "Unable to determine device bus information from the"

108 " pacmd list-%ss output\npacmd output was: %s"

109 % (vtype, pacmd_entries),

110 file=sys.stderr,

111 )

112 return 1

113

114 volume_match = volume_regex.search(entry)

115 if volume_match:

116 volume = int(volume_match.group().strip())

117 if volume > maxvol:

118 print(

119 "FAIL: Volume of %d is greater than"

120 " maximum of %d for %s %s"

121 % (volume, maxvol, name, vtype)

122 )

123 retval = 1

124 elif volume < minvol:

125 print(

126 "FAIL: Volume of %d is less than"

127 " minimum of %d for %s %s"

128 % (volume, minvol, name, vtype)

129 )

130 retval = 1

131 else:

132 print(

133 "PASS: Volume is %d for %s %s" % (volume, name, vtype)

134 )

135 else:

136 print(

137 "Unable to find volume information in the pacmd"

138 " list-%ss output for device %s.\npacmd output "

139 "was: %s" % (vtype, name, pacmd_entries),

140 file=sys.stderr,

141 )

142 return 1

143 return retval

144

145

146 def main():

147 parser = ArgumentParser("Check the audio volume")

148 parser.add_argument(

149 "-n",

150 "--minvol",

151 type=int,

152 required=True,

153 help="""The minimum volume for a check_volume call.

154 Volume must be greater than this number to

155 be considered a pass.""",

156 )

157 parser.add_argument(

158 "-x",

159 "--maxvol",

160 type=int,

161 required=True,

162 help="""The maximum volume for a check_volume call.

163 Volume must be less than this number to

164 be considered a pass.""",

165 )

166 args = parser.parse_args()

167

168 check_muted_retval = check_muted()

169 check_volume_retval = check_volume(args.minvol, args.maxvol)

170 return check_muted_retval or check_volume_retval

171

172

173 if __name__ == "__main__":

174 sys.exit(main())