1#!/usr/bin/env python3

2# This file is part of Checkbox.

3#

4# Copyright 2020 Canonical Ltd.

5# Written by:

6# Jonathan Cave <jonathan.cave@canonical.com>

7#

8# Checkbox is free software: you can redistribute it and/or modify

9# it under the terms of the GNU General Public License version 3,

10# as published by the Free Software Foundation.

11#

12# Checkbox is distributed in the hope that it will be useful,

13# but WITHOUT ANY WARRANTY; without even the implied warranty of

14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

15# GNU General Public License for more details.

16#

17# You should have received a copy of the GNU General Public License

18# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.

19

20

21 import os

22 import random

23 import string

24 import time

25 import subprocess as sp

26 import sys

27

28 import pyqrcode

29 import zbar

30 from PIL import Image

31

32

33 def capture_rpi(name):

34 import picamera

35

36 file = os.path.join(

37 os.path.expandvars("$PLAINBOX_SESSION_SHARE"),

38 "{}_qrcapture.png".format(name),

39 )

40 with picamera.PiCamera() as camera:

41 time.sleep(2)

42 camera.capture(file)

43 return file

44

45

46 def capture_webcam(name):

47 file = os.path.join(

48 os.path.expandvars("$PLAINBOX_SESSION_SHARE"),

49 "{}_qrcapture.jpg".format(name),

50 )

51 cmd = (

52 "gst-launch-1.0 v4l2src device=/dev/{} num-buffers=1 ! jpegenc !"

53 "filesink location={}"

54 ).format(name, file)

55 try:

56 sp.check_call(cmd, shell=True)

57 except (sp.CalledProcessError, OSError) as e:

58 print(e)

59 raise SystemExit("ERROR: failed to capture image")

60 return file

61

62

63 def generate_data():

64 return "".join(random.choice(string.ascii_letters) for i in range(10))

65

66

67 def generate_qr_code(data):

68 return pyqrcode.create(data)

69

70

71 def display_code(qr):

72 with open("/dev/tty0", "wb+", buffering=0) as term:

73 # clear the tty so the qr is always printed at the top of the sceen

74 term.write(str.encode("\033c"))

75 # print the qr code

76 term.write(qr.terminal(quiet_zone=1).encode())

77

78

79 def decode_image(filename):

80 scanner = zbar.ImageScanner()

81 scanner.parse_config("enable")

82 pil = Image.open(filename).convert("L")

83 width, height = pil.size

84 raw = pil.tobytes()

85 image = zbar.Image(width, height, "Y800", raw)

86 scanner.scan(image)

87 result = None

88 for code in image:

89 result = code.data

90 del image

91 if result is None:

92 raise SystemExit("ERROR: no qrcodes decoded")

93 return result

94

95

96 def main():

97 if len(sys.argv) != 2:

98 raise SystemExit("ERROR: expected a device name")

99 name = sys.argv[1]

100 print("Testing device name: {}\n".format(name))

101

102 test_str = generate_data()

103 print("Input string: {}".format(test_str), flush=True)

104

105 print("Generating QR code...", flush=True)

106 qr = generate_qr_code(test_str)

107

108 print("Displaying on screen", flush=True)

109 display_code(qr)

110

111 print("Capture image of screen", flush=True)

112 if name == "vchiq":

113 file = capture_rpi(name)

114 else:

115 file = capture_webcam(name)

116 print("Image {} captured".format(file))

117

118 print("Decoding image file", flush=True)

119 result = decode_image(file)

120 print("Decoded data: {}".format(result))

121

122 if result != test_str:

123 raise SystemExit("FAIL: decoded data does not match input")

124 print("PASS: decoded data and input match")

125

126

127 if __name__ == "__main__":

128 main()