diff --git a/certs/cacert.pem b/certs/cacert.pem new file mode 100644 index 0000000..e104d9b --- /dev/null +++ b/certs/cacert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXjCCAkagAwIBAgIJAKLZNiSf8lO7MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJNTjENMAsGA1UEBwwETXBsczEQMA4GA1UECgwHRXhv +c2l0ZTEYMBYGA1UEAwwPKi5tMi5leG9zaXRlLmlvMB4XDTE2MDkxNTAzMjc1OVoX +DTI2MDkxMzAzMjc1OVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1OMQ0wCwYD +VQQHDARNcGxzMRAwDgYDVQQKDAdFeG9zaXRlMRgwFgYDVQQDDA8qLm0yLmV4b3Np +dGUuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnaAQqYbKFiFxZ +gBCI5hl8cT3pV53r1cj5Z4ZnFQroGdZHJFe1OPiDLUKW6+uWlMee06wwv7E1E1UT +IfhwWIEweHnCZh4F2jGVW0R+t0VyWIHgEqoObpHjOFGGBBGHex0H1LleTtg6WiJ0 +gOgpDTOtsSRHwNm5q+J9WREwWH9uDkmJK3OZUrSXhYgtiebgltL4jIU3wVEs3oXt +eeyWFyE+g/V1fDELvfhajoieeM6K/8nbyxC/qfUrwqQnQ93zPr0FxQlR2nOcL1ic ++EsanOwYUAjbfGPRhbjfS3f1IimbfiPAyXSLCSzC2QoWi572SnATJzjraenn9mI3 +ru95Sg9BAgMBAAGjMTAvMC0GA1UdEQQmMCSCDyoubTIuZXhvc2l0ZS5pb4IRKi5h +cHBzLmV4b3NpdGUuaW8wDQYJKoZIhvcNAQELBQADggEBAHSsfLLoPyc4LmaYOjYw +nrVD/xM6Ehj5q/P53qOoHgDktMdApnQypjloaaYd9BeuAxMmXCke3g61ULgJkACY +w+Uu9C5aidND37jZXjvJN1oVPOW4wk8z9FIWGOysejgQwoChxqnaCQtujtjtY1Xp +o9YnjUfq1IfTCO98TeNK+VN84u9q6ZlEmFfhxVS7AV10YK3Zn8IszhMBvQQ5T0Y4 +PrkajgK91uRGAr+LHchBrIqzcobDcdx0wGwPtmu9nuTPeO98gLQ2CsW9eK9sGTWx +BqxYsLJC66Fd4y30SHm+2cnXRS62RdB2aCDw1sLW6v1LxXGix+dedtxSFhCl3rgL +pEs= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/certs/clcert.pem b/certs/clcert.pem new file mode 100644 index 0000000..3d7e3ec --- /dev/null +++ b/certs/clcert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHTCCAgWgAwIBAgIJAMueV5//ekUZMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV +BAMTCEJFRUZDQTdFMB4XDTE2MDkyMzE4MTE0OVoXDTM1MTEyMzE4MTE0OVowEzER +MA8GA1UEAxMIQkVFRkNBN0UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDe2CE0izxIMSS9aGpzWq6OnlvaUEqMCc59HPejtBKVLL0e4ldRjgfVJuSJDpQa +oW1KP/RsSLjkAsCsXcwJeLQs2uwb08rjMDwW/nuuFL1MKGt4vAHgzONedHKc4FeF +/2JSuy+LAV6cu7GXwi1AHUKKmvU052fOSKyAUofrDMiKDKNOg00NEgukB6rvddmh +ptlqikkMqkksPOuhq1WpqPQgmXS4h34hqcwpWf+6VlC3MAfJHr9nxPVWYQ+QbspG +tRxI7TiaIbIwfgtwxWLxMCM5Os3c8d4Bt2Fmp6W6xlBwKw5CHS8klACcClOjbmh4 +vuic2RVw8oHw+5ECcJx/cA2tAgMBAAGjdDByMB0GA1UdDgQWBBQABl/ttUqA7j/V +hUni1M+rZkpUvDBDBgNVHSMEPDA6gBQABl/ttUqA7j/VhUni1M+rZkpUvKEXpBUw +EzERMA8GA1UEAxMIQkVFRkNBN0WCCQDLnlef/3pFGTAMBgNVHRMEBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAjk/rwjH9LBrkiCub8FthFGcGHtAN6ognD2RdA2J0g +Ohfk4v2nFdHE8BVTUB3NwoIPQxMosOeIzKXCGOh/8z2kGiC4BM2l4C9fkJyB/i9n +V04DpNTyDxCzgO3wu4HmcEaMJwmSCb/xPw8VnIvk6Ztq0xy/1BWuPRf/p2/RWWBM +Wy+VFssswLQ/b2O38Yats+E1dcNvKCkCkZpFlv73dbWbv/tmGHyGj5IKwEvF7ReT +AOwvPpW36PntSjzek4tFKFi8HcVoLvB0xy6ELY/Q2CX98BhFCaY3II6ePRgLnk9k +SrpMdFIiGbGNde2KYZSgC/N34/htRYg63wyHPXmEAQlG +-----END CERTIFICATE----- diff --git a/certs/clpkey.pem b/certs/clpkey.pem new file mode 100644 index 0000000..7555bb1 --- /dev/null +++ b/certs/clpkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA3tghNIs8SDEkvWhqc1qujp5b2lBKjAnOfRz3o7QSlSy9HuJX +UY4H1SbkiQ6UGqFtSj/0bEi45ALArF3MCXi0LNrsG9PK4zA8Fv57rhS9TChreLwB +4MzjXnRynOBXhf9iUrsviwFenLuxl8ItQB1Cipr1NOdnzkisgFKH6wzIigyjToNN +DRILpAeq73XZoabZaopJDKpJLDzroatVqaj0IJl0uId+IanMKVn/ulZQtzAHyR6/ +Z8T1VmEPkG7KRrUcSO04miGyMH4LcMVi8TAjOTrN3PHeAbdhZqelusZQcCsOQh0v +JJQAnApTo25oeL7onNkVcPKB8PuRAnCcf3ANrQIDAQABAoIBAQCc2SMGCLp1VaG2 +bzf92WXjnDKqhrjxuDVOeoUTBDvQI505j7ZGAOIidS2GCmzUEasLB9jSHp2EXyue +JqbmQ78Nk0l8jKXRIrIovRXwgUh7wjdi03G++HdQlEXXhVAVUJuz/2yHOn+Ncc6e +Ml4HMpETXd4ZTocR+rTv3SctmeHMx6esPp9bronccuXvhKonQSGhNXNvcyOKoJlO +k2mttgOZtq0od/DKn+w2ykmJ6bXrYYCv+A54UIXNKbv6JJpfQ/IkxlSPB4QUlVxg +/sJMnpBZYFgPuff8wLXtsgm8xR+C4bnw78rk3k5N4aWPwLOKA2qi6ga3c2dUMaME +cKANHZVBAoGBAPxoOfGZ4jF/8ofNxIRIln5hAEaBX9ffVrlQM4Y/AyH0wiU4Lyjt +M39FQBBB0soJU9KJ0pT+Pp4Oe6m8B4j5su3xt60ZkF2453gKriX2GCTcJDp7R9IG +vWMyJMVY2A4QNxD2CyAmGThqZ0l6f9tdYFPfUAZ7t1BvWrjqvw0M3vBDAoGBAOIE +LQ46i+c4ykTFpAyOuRu2YunrmTA5m384chTZYOhfMndgLCZOYv5blc34oQHmeJmq +9YJRjipc2sfAJ5AhazgmDsckxiWD95/4/Kt4HT/Z7pv+jmNoU06p2xlOwcr88DBX +HbbLGq/LyaWidZedPW8k9RH4BDw1ZCf5v74qvmNPAoGAeCqi03xArPJOJAt5sMTi +KR7DQ83aNGsW3bjqHtKnCiZ9u7yI70Imj0QH4PWFhjx0lLqa7+YvJ46bn05ug+7l +GdVHbfjKu2QJhyQGjvofuoS4FsOUiNA+oBhCW1YV3nQn00JgP5kDztERhXD1qBlH +gAbEEaIavweUuI7CEFN9XAMCgYBxbx8d0Qx+U+ZQddFL7CXDXtpuBKyxo3gDddTr +2d3lQkIV39LItvbAKj8ZTPuh9IX6ue7WJRBw+oFjV3GDyQIDADbShAZetckGJPTL +KlGjxkEXUb8s7SNCi8VHAlwJAMGbwV1MbMtMB7+AvfS+z1ASBD5rckrN3Q4Tcill +zBYZyQKBgCtPFcjoXXRnsoKKKv/Wh+gnOT95s7LSeouHtL3L06QsXZ/9H2XsCIBe +UPmjNRPtf0YdwLiVlJVGQJMrE73JOMsycdUBzhIrj1f0E5nmuoFcdU0/PgKDEvzo +aeb6k7e7h9HjhzpK/RCMX1+By4XI2NOA17hl1UvJlIPv5Zt93JHH +-----END RSA PRIVATE KEY----- diff --git a/murano_device_simulator.py b/murano_device_simulator.py index 9688d76..976917c 100755 --- a/murano_device_simulator.py +++ b/murano_device_simulator.py @@ -27,20 +27,13 @@ import datetime import random -import socket -import ssl +import requests try: - from StringIO import StringIO - import httplib input = raw_input - PYTHON = 2 except ImportError: - from http import client as httplib - from io import StringIO, BytesIO - - PYTHON = 3 + pass # ----------------------------------------------------------------- # EXOSITE PRODUCT ID / SERIAL NUMBER IDENTIFIER / CONFIGURATION @@ -49,7 +42,7 @@ productid = os.getenv('SIMULATOR_PRODUCT_ID', UNSET_PRODUCT_ID) identifier = os.getenv('SIMULATOR_DEVICE_ID', '000001') # default identifier -SHOW_HTTP_REQUESTS = False +SHOW_RAW_HTTP = str(os.getenv('SHOW_RAW_HTTP')).lower() == "true" PROMPT_FOR_PRODUCTID_AND_SN = os.getenv('SIMULATOR_SHOULD_PROMPT', '1') == '1' LONG_POLL_REQUEST_TIMEOUT = 2 * 1000 # in milliseconds @@ -57,22 +50,11 @@ # ---- SHOULD NOT NEED TO CHANGE ANYTHING BELOW THIS LINE ------ # ----------------------------------------------------------------- +host_address_base = os.getenv('SIMULATOR_HOST', 'm2.exosite.com') host_address_base = os.getenv('SIMULATOR_HOST', 'm2.exosite.com') host_address = None # set this later when we know the product ID https_port = 443 - -class FakeSocket: - def __init__(self, response_str): - if PYTHON == 2: - self._file = StringIO(response_str) - else: - self._file = BytesIO(response_str) - - def makefile(self, *args, **kwargs): - return self._file - - # LOCAL DATA VARIABLES FLAG_CHECK_ACTIVATION = False @@ -85,121 +67,61 @@ def makefile(self, *args, **kwargs): # -# DEVICE MURANO RELATED FUNCTIONS +# HELPER FUNCTIONS # -def SOCKET_SEND(http_packet): - # SEND REQUEST - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ssl_s = ssl.wrap_socket(s) - ssl_s.connect((host_address, https_port)) - if SHOW_HTTP_REQUESTS: - print("--- Sending ---\r\n {} \r\n----".format(http_packet)) - if PYTHON == 2: - ssl_s.send(http_packet) - else: - ssl_s.send(bytes(http_packet, 'UTF-8')) - # GET RESPONSE - response = ssl_s.recv(1024) - ssl_s.close() - if SHOW_HTTP_REQUESTS: - print("--- Response --- \r\n {} \r\n---") - - # PARSE REPONSE - fake_socket_response = FakeSocket(response) - parsed_response = httplib.HTTPResponse(fake_socket_response) - parsed_response.begin() - return parsed_response - - -def ACTIVATE(): - try: - # print("attempt to activate on Murano") - - http_body = 'vendor=' + productid + '&model=' + productid + '&sn=' + identifier - # BUILD HTTP PACKET - http_packet = "" - http_packet += 'POST /provision/activate HTTP/1.1\r\n' - http_packet += 'Host: ' + host_address + '\r\n' - http_packet += 'Connection: Close \r\n' - http_packet += 'Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n' - http_packet += 'content-length:' + str(len(http_body)) + '\r\n' - http_packet += '\r\n' - http_packet += http_body - - response = SOCKET_SEND(http_packet) - - # HANDLE POSSIBLE RESPONSES - if response.status == 200: - new_cik = response.read().decode("utf-8") - print("Activation Response: New CIK: {} ..............................".format(new_cik[0:10])) - return new_cik - elif response.status == 409: - print("Activation Response: Device Aleady Activated, there is no new CIK") - elif response.status == 404: - print("Activation Response: Device Identity ({}) activation not available or check Product Id ({})".format( - identifier, - productid - )) - else: - print("Activation Response: failed request: {} {}".format(str(response.status), response.reason)) - return None - - except Exception as e: - # pass - print("Exception: {}".format(e)) - return None +def pretty_print_request(req): + print('{}\n{}\n{}\n\n{}\n{}'.format( + '-----------RAW REQUEST------------', + req.method + ' ' + req.url, + '\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()), + req.body if req.body != None else '', + '----------------------------------', + )) + +def pretty_print_respone(rsp): + print('{}\n{}\n{}\n\n{}\n{}'.format( + '-----------RAW RESPONSE-----------', + 'HTTP/1.1 ' + str(rsp.status_code) + ' ' + rsp.reason, + '\n'.join('{}: {}'.format(k, v) for k, v in rsp.headers.items()), + rsp.text if rsp.text != None else '', + '----------------------------------', + )) -def GET_STORED_CIK(): - print("get stored CIK from non-volatile memory") - try: - f = open(productid + "_" + identifier + "_cik", "r+") # opens file to store CIK - local_cik = f.read() - f.close() - print("Stored cik: {} ..............................".format(local_cik[0:10])) - return local_cik - except Exception as e: - print("Unable to read a stored CIK: {}".format(e)) - return None +# +# DEVICE MURANO RELATED FUNCTIONS +# +def WRITE(WRITE_PARAMS): + request = requests.Request( + 'POST', + "https://" + host_address + "/onep:v1/stack/alias", + headers = { + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" + } + ).prepare() -def STORE_CIK(cik_to_store): - print("storing new CIK to non-volatile memory") - f = open(productid + "_" + identifier + "_cik", "w") # opens file that stores CIK - f.write(cik_to_store) - f.close() - return True + if SHOW_RAW_HTTP: + pretty_print_request(request) + # send with global session + response = s.send(request) -def WRITE(WRITE_PARAMS): - # print "write data to Murano" - - http_body = WRITE_PARAMS - # BUILD HTTP PACKET - http_packet = "" - http_packet += 'POST /onep:v1/stack/alias HTTP/1.1\r\n' - http_packet += 'Host: ' + host_address + '\r\n' - http_packet += 'X-EXOSITE-CIK: ' + cik + '\r\n' - http_packet += 'Connection: Close \r\n' - http_packet += 'Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n' - http_packet += 'content-length:' + str(len(http_body)) + '\r\n' - http_packet += '\r\n' - http_packet += http_body - - response = SOCKET_SEND(http_packet) + if SHOW_RAW_HTTP: + pretty_print_respone(response) # HANDLE POSSIBLE RESPONSES - if response.status == 204: + if response.status_code == 204: # print "write success" return True, 204 - elif response.status == 401: - print("401: Bad Auth, CIK may be bad") + elif response.status_code == 401: + print("401: Bad Auth") return False, 401 - elif response.status == 400: + elif response.status_code == 400: print("400: Bad Request: check syntax") return False, 400 - elif response.status == 405: + elif response.status_code == 405: print("405: Bad Method") return False, 405 else: @@ -207,44 +129,49 @@ def WRITE(WRITE_PARAMS): return False, response.status # This code is unreachable and should be removed - # except Exception as err: + # except Exception as err: # pass # print("exception: {}".format(str(err))) # return None + def READ(READ_PARAMS): try: - # print("read data from Murano") + request = requests.Request( + 'GET', + "https://" + host_address + "/onep:v1/stack/alias?" + READ_PARAMS, + headers = { + "Accept": "application/x-www-form-urlencoded; charset=utf-8" + } + ).prepare() + + if SHOW_RAW_HTTP: + pretty_print_request(request) - # BUILD HTTP PACKET - http_packet = "" - http_packet += 'GET /onep:v1/stack/alias?' + READ_PARAMS + ' HTTP/1.1\r\n' - http_packet += 'Host: ' + host_address + '\r\n' - http_packet += 'X-EXOSITE-CIK: ' + cik + '\r\n' - # http_packet += 'Connection: Close \r\n' - http_packet += 'Accept: application/x-www-form-urlencoded; charset=utf-8\r\n' - http_packet += '\r\n' + # send with global session + response = s.send(request) - response = SOCKET_SEND(http_packet) + if SHOW_RAW_HTTP: + pretty_print_respone(response) # HANDLE POSSIBLE RESPONSES - if response.status == 200: + if response.status_code == 200: # print "read success" - return True, response.read().decode('utf-8') - elif response.status == 401: - print("401: Bad Auth, CIK may be bad") + return True, response.text + elif response.status_code == 401: + print("401: Bad Auth") return False, 401 - elif response.status == 400: + elif response.status_code == 400: print("400: Bad Request: check syntax") return False, 400 - elif response.status == 405: + elif response.status_code == 405: print("405: Bad Method") return False, 405 else: - print(str(response.status), response.reason, 'failed:') - return False, response.status + print(str(response.status_code), response.text, 'failed:') + return False, response.status_code except Exception as e: # pass @@ -254,39 +181,48 @@ def READ(READ_PARAMS): def LONG_POLL_WAIT(READ_PARAMS): try: - # print "long poll state wait request from Murano" - # BUILD HTTP PACKET - http_packet = "" - http_packet += 'GET /onep:v1/stack/alias?' + READ_PARAMS + ' HTTP/1.1\r\n' - http_packet += 'Host: ' + host_address + '\r\n' - http_packet += 'Accept: application/x-www-form-urlencoded; charset=utf-8\r\n' - http_packet += 'X-EXOSITE-CIK: ' + cik + '\r\n' - http_packet += 'Request-Timeout: ' + str(LONG_POLL_REQUEST_TIMEOUT) + '\r\n' + headers = { + "Accept": "application/x-www-form-urlencoded; charset=utf-8", + 'Request-Timeout': str(LONG_POLL_REQUEST_TIMEOUT), + } + if last_modified.get(READ_PARAMS) != None: - http_packet += 'If-Modified-Since: ' + last_modified.get(READ_PARAMS) + '\r\n' - http_packet += '\r\n' + headers['If-Modified-Since'] = last_modified.get(READ_PARAMS) + + request = requests.Request( + 'GET', + "https://" + host_address + "/onep:v1/stack/alias?" + READ_PARAMS, + headers = headers + ).prepare() + + if SHOW_RAW_HTTP: + pretty_print_request(request) + + # send with global session + response = s.send(request) - response = SOCKET_SEND(http_packet) + if SHOW_RAW_HTTP: + pretty_print_respone(response) # HANDLE POSSIBLE RESPONSES - if response.status == 200: + if response.status_code == 200: # print "read success" - if response.getheader("last-modified") != None: + if response.headers.get("last-modified") != None: # Save Last-Modified Header (Plus 1s) - lm = response.getheader("last-modified") + lm = response.headers.get("last-modified") next_lm = (datetime.datetime.strptime(lm, "%a, %d %b %Y %H:%M:%S GMT") + datetime.timedelta(seconds=1)).strftime("%a, %d %b %Y %H:%M:%S GMT") last_modified[READ_PARAMS] = next_lm - return True, response.read() - elif response.status == 304: + return True, response.text + elif response.status_code == 304: # print "304: No Change" return False, 304 - elif response.status == 401: - print("401: Bad Auth, CIK may be bad") + elif response.status_code == 401: + print("401: Bad Auth") return False, 401 - elif response.status == 400: + elif response.status_code == 400: print("400: Bad Request: check syntax") return False, 400 - elif response.status == 405: + elif response.status_code == 405: print("405: Bad Method") return False, 405 else: @@ -308,7 +244,6 @@ def LONG_POLL_WAIT(READ_PARAMS): # BOOT # -------------------------- -# Check if CIK locally stored already if PROMPT_FOR_PRODUCTID_AND_SN is True or productid == UNSET_PRODUCT_ID: print("Check for Device Parameters Enabled (hit return after each question)") productid = input("Enter the Murano Product ID: ") @@ -317,7 +252,7 @@ def LONG_POLL_WAIT(READ_PARAMS): print("The Host Address is: {}".format(host_address)) # hostok = input("If OK, hit return, if you prefer a different host address, type it here: ") # if hostok != "": - # host_address = hostok + # host_address = hostok print("The default Device Identity is: {}".format(identifier)) identityok = input("If OK, hit return, if you prefer a different Identity, type it here: ") @@ -333,16 +268,10 @@ def LONG_POLL_WAIT(READ_PARAMS): print("Device Identity: {}".format(identifier)) print("Product Unique Host: {}".format(host_address)) print("-----") -cik = GET_STORED_CIK() -if cik is None: - print("try to activate") - act_response = ACTIVATE() - if act_response is not None: - cik = act_response - STORE_CIK(cik) - FLAG_CHECK_ACTIVATION = False - else: - FLAG_CHECK_ACTIVATION = True + +# global requests network session +s = requests.Session() +s.cert = ('./certs/clcert.pem', './certs/clpkey.pem') # -------------------------- # MAIN LOOP @@ -369,19 +298,19 @@ def LONG_POLL_WAIT(READ_PARAMS): else: print("Light Bulb is Off") -while LOOP: - uptime = int(time.time()) - start_time - last_request = time.time() +try: + while LOOP: + uptime = int(time.time()) - start_time + last_request = time.time() - connection = 'Connected' - if FLAG_CHECK_ACTIVATION: - connection = "Not Connected" + connection = 'Connected' + if FLAG_CHECK_ACTIVATION: + connection = "Not Connected" - output_string = ( - "Connection: {0:s}, Run Time: {1:5d}, Temperature: {2:3.1f} F, Humidity: {3:3.1f} %, Light State: {4:1d}").format(connection, uptime, temperature, humidity, lightbulb_state) - print("{}".format(output_string)) + output_string = ( + "Connection: {0:s}, Run Time: {1:5d}, Temperature: {2:3.1f} F, Humidity: {3:3.1f} %, Light State: {4:1d}").format(connection, uptime, temperature, humidity, lightbulb_state) + print("{}".format(output_string)) - if cik is not None and not FLAG_CHECK_ACTIVATION: # GENERATE RANDOM TEMPERATURE VALUE temperature = round(random.uniform(temperature - 0.2, temperature + 0.2), 1) @@ -398,12 +327,14 @@ def LONG_POLL_WAIT(READ_PARAMS): status, resp = WRITE('temperature=' + str(temperature) + '&humidity=' + str(humidity) + '&uptime=' + str(uptime)) if not status and resp == 401: - FLAG_CHECK_ACTIVATION = True + time.sleep(3) + continue # print("Look for on/off state change") status, resp = LONG_POLL_WAIT('state') if not status and resp == 401: - FLAG_CHECK_ACTIVATION = True + time.sleep(3) + continue if not status and resp == 304: # print("No New State Value") pass @@ -418,15 +349,6 @@ def LONG_POLL_WAIT(READ_PARAMS): else: print("Action -> Turn Light Bulb Off") - if FLAG_CHECK_ACTIVATION: - if (uptime % 10) == 0: - # print("---") - print("Device CIK may be expired or not available (not added to product) - trying to activate") - act_response = ACTIVATE() - if act_response is not None: - cik = act_response - STORE_CIK(cik) - FLAG_CHECK_ACTIVATION = False - else: - # print("Wait 10 seconds and attempt to activate again") - time.sleep(1) +# Catch 'Ctrl+C' to not print stack trace +except KeyboardInterrupt: + pass