diff --git a/css/extension.css b/css/extension.css index 1cdb135..6aeb497 100644 --- a/css/extension.css +++ b/css/extension.css @@ -221,6 +221,24 @@ /* PRINTER */ + +#extension-privacy-manager-pp-header{ + width: 40%; + margin: 0 auto; + display: block; +} + + + +.extension-privacy-manager-show-if-printer-connected{ + display:none; +} + +.extension-privacy-manager-printer-connected .extension-privacy-manager-show-if-printer-connected{ + display:initial; +} + + #extension-privacy-manager-printer-connected{ display:inline-block; padding: 0 .5rem; @@ -253,6 +271,7 @@ #extension-privacy-manager-print-test-button, #extension-privacy-manager-start-bluetooth-scan-button{ margin-left:1rem; + display:none; } @@ -272,6 +291,23 @@ } +#extension-privacy-manager-printable-icons-list{ + display:flex; + flex-wrap:wrap; + justify-content:center; +} + +#extension-privacy-manager-printable-icons-list img{ + width:200px; + max-width:50%; + margin:1rem; + cursor:pointer; + opacity:.9; +} + +#extension-privacy-manager-printable-icons-list img:hover{ + opacity:1; +} diff --git a/images/closed-eye-10min.png b/images/closed-eye-10min.png new file mode 100644 index 0000000..7d388bc Binary files /dev/null and b/images/closed-eye-10min.png differ diff --git a/images/closed-eye-1hour.png b/images/closed-eye-1hour.png new file mode 100644 index 0000000..fac9862 Binary files /dev/null and b/images/closed-eye-1hour.png differ diff --git a/images/closed-eye-20min.png b/images/closed-eye-20min.png new file mode 100644 index 0000000..30189e4 Binary files /dev/null and b/images/closed-eye-20min.png differ diff --git a/images/closed-eye-2hours.png b/images/closed-eye-2hours.png new file mode 100644 index 0000000..b91541a Binary files /dev/null and b/images/closed-eye-2hours.png differ diff --git a/images/closed-eye-4hours.png b/images/closed-eye-4hours.png new file mode 100644 index 0000000..2b3962d Binary files /dev/null and b/images/closed-eye-4hours.png differ diff --git a/images/closed-eye-8hours.png b/images/closed-eye-8hours.png new file mode 100644 index 0000000..cc01479 Binary files /dev/null and b/images/closed-eye-8hours.png differ diff --git a/images/closed-eye-icon-big.png b/images/closed-eye-icon-big.png new file mode 100644 index 0000000..0417543 Binary files /dev/null and b/images/closed-eye-icon-big.png differ diff --git a/images/closed-eye-icon.png b/images/closed-eye-icon.png new file mode 100644 index 0000000..ab08047 Binary files /dev/null and b/images/closed-eye-icon.png differ diff --git a/images/pp-printer-icon.svg b/images/pp-printer-icon.svg new file mode 100644 index 0000000..cc4197d --- /dev/null +++ b/images/pp-printer-icon.svg @@ -0,0 +1,8 @@ + diff --git a/js/extension.js b/js/extension.js index a1febda..c48ea63 100644 --- a/js/extension.js +++ b/js/extension.js @@ -426,8 +426,6 @@ const bluetooth_scan_button = document.getElementById('extension-privacy-manager-start-bluetooth-scan-button'); pre.innerText = ""; - - API.getThings().then((things) => { for (let key in things) { @@ -449,19 +447,7 @@ } console.log("complete things lookup table: ", this.thing_title_lookup_table); }); - /* - API.getThings().then((things) => { - console.log("all things: "); - console.log(things); - - - //console.log("this.all_things: ", this.all_things); - //console.log("this.title_lookup_table: ", this.title_lookup_table); - - //this.request_devices_list(); - }); - */ - + // TABS @@ -483,20 +469,6 @@ //this.scan_for_printer(); this.show_printer_state(); }); - /* - - // Quick templates - tab_button_quick.addEventListener('click', () => { - this.addClass(tab_button_quick, "extension-privacy-manager-button-active"); - this.removeClass(tab_button_internal, "extension-privacy-manager-button-active"); - this.removeClass(tab_button_sculptor, "extension-privacy-manager-button-active"); - - this.addClass(tab_internal, "extension-privacy-manager-hidden"); - this.addClass(tab_sculptor, "extension-privacy-manager-hidden"); - this.removeClass(tab_quick, "extension-privacy-manager-hidden"); - }); - */ - // Data sculptor tab tab_button_sculptor.addEventListener('click', () => { @@ -597,6 +569,7 @@ document.getElementById('extension-privacy-manager-print-test-button').style.display = 'block'; }).catch((e) => { console.log("Privacy manager: error in print test: ", e); + alert('Error: could not connect to the controller'); document.getElementById('extension-privacy-manager-print-test-progress').style.display = 'none'; document.getElementById('extension-privacy-manager-print-test-button').style.display = 'block'; this.show_printer_connection_state(false); @@ -634,6 +607,45 @@ }); + + // Print icon + document.getElementById('extension-privacy-manager-printable-icons-list').addEventListener('click', (event) => { + console.log("print icon button clicked"); + if(confirm("Are you sure you want to print this icon?")){ + + const filename = event.target.getAttribute('data-filename'); + console.log("filename: ", filename); + + window.API.postJson( + `/extensions/${this.id}/api/print_image`, //,{'printer_log':printer_log, 'printer_interval':printer_interval} + {'filename':filename} + ).then((body) => { + console.log(body); + + if(body.state == 'error'){ + alert("Error, could not print the file") + } + else{ + console.log("image was sent to printer"); + } + + }).catch((e) => { + console.log("Privacy manager: error in print icon: ", e); + alert('Could not print icon - connection error'); + }); + } + + }); + + + + + + + // + // DATA SCULPTOR BUTTONS + // + // CHANGE POINT @@ -697,7 +709,10 @@ if(typeof body['persistent']['duration'] != 'undefined'){ console.log("setting dropdown to duration: ", body['persistent']['duration']); - document.getElementById('extension-privacy-manager-quick-delete-time-dropdown').value = body['persistent']['duration']; + if(document.getElementById('extension-privacy-manager-quick-delete-time-dropdown') != null){ + document.getElementById('extension-privacy-manager-quick-delete-time-dropdown').value = body['persistent']['duration']; + } + } } @@ -715,7 +730,6 @@ }).catch((e) => { - //pre.innerText = e.toString(); console.log("Privacy manager: error in show call to api/init: ", e); }); @@ -787,16 +801,16 @@ fresh_date.setMilliseconds(document.getElementById('extension-privacy-manager-input-millis').value); /* - console.log(document.getElementById('extension-privacy-manager-input-year').value); - console.log(document.getElementById('extension-privacy-manager-input-month').value); - console.log(document.getElementById('extension-privacy-manager-input-day').value); - console.log(document.getElementById('extension-privacy-manager-input-hour').value); - console.log(document.getElementById('extension-privacy-manager-input-minute').value); - console.log(document.getElementById('extension-privacy-manager-input-second').value); - console.log(document.getElementById('extension-privacy-manager-input-millis').value); + console.log(document.getElementById('extension-privacy-manager-input-year').value); + console.log(document.getElementById('extension-privacy-manager-input-month').value); + console.log(document.getElementById('extension-privacy-manager-input-day').value); + console.log(document.getElementById('extension-privacy-manager-input-hour').value); + console.log(document.getElementById('extension-privacy-manager-input-minute').value); + console.log(document.getElementById('extension-privacy-manager-input-second').value); + console.log(document.getElementById('extension-privacy-manager-input-millis').value); - console.log("new date stamp: " + fresh_date.valueOf() ); - */ + console.log("new date stamp: " + fresh_date.valueOf() ); + */ return fresh_date.valueOf(); } @@ -811,9 +825,9 @@ var new_date_stamp = this.get_new_date(); // reconnect all the pieces from the dropdowns (and the hidden milliseconds value) into the new date /* console.log("____action = " + action); - console.log("property = " + updating_property_id); + console.log("property = " + updating_property_id); console.log("of type = " + updating_data_type); - console.log("old_date_stamp = " + old_date_stamp); + console.log("old_date_stamp = " + old_date_stamp); console.log("new_date_stamp = " + new_date_stamp); */ @@ -904,7 +918,7 @@ this.display_thing_data(updating_property_id, updating_data_type, body['data']); }).catch((e) => { - console.log("Privacy manager: error in deletion handler"); + console.log("Privacy manager: error in deletion handler: ", e); pre.innerText = e.toString(); }); @@ -972,7 +986,7 @@ this.show_internal_logs(body['data']); }).catch((e) => { - console.log("Privacy manager: error in internal log deletion handler"); + console.log("Privacy manager: error in internal log deletion handler: ", e); pre.innerText = e.toString(); }); @@ -1049,10 +1063,12 @@ if(connected){ document.getElementById('extension-privacy-manager-printer-connected').innerHTML = "⇄"; document.getElementById('extension-privacy-manager-printer-connected').style.background = "green"; + document.getElementById('extension-privacy-manager-view').classList.add('extension-privacy-manager-printer-connected'); } else{ document.getElementById('extension-privacy-manager-printer-connected').innerHTML = "?"; document.getElementById('extension-privacy-manager-printer-connected').style.background = "red"; + document.getElementById('extension-privacy-manager-view').classList.remove('extension-privacy-manager-printer-connected'); } } @@ -1067,10 +1083,11 @@ ).then((body) => { //console.log(body); if(body.scanning){ - console.log("still scanning for a printer"); + document.getElementById('extension-privacy-manager-start-bluetooth-scan-button').style.display = 'none'; } else{ console.log("not currently scanning for a printer"); + document.getElementById('extension-privacy-manager-start-bluetooth-scan-button').style.display = 'block'; } @@ -1091,7 +1108,7 @@ if( typeof body['persistent']['printer_log_name'] != 'undefined'){ if(body['persistent']['printer_log_name'] != "None"){ document.getElementById('extension-privacy-manager-selected-log-to-print').innerText = body['persistent']['printer_log_name']; - document.getElementById('extension-privacy-manager-clean-slate-container').style.display = 'block'; + //document.getElementById('extension-privacy-manager-clean-slate-container').style.display = 'block'; } } @@ -1139,7 +1156,7 @@ for (var key in logs_list) { //console.log(key); - console.log("logs list: ", logs_list[key]); + //console.log("logs list: ", logs_list[key]); var dataline = JSON.parse(logs_list[key]['name']); //console.log(Object.keys(dataline)); @@ -1157,7 +1174,7 @@ var new_option = new Option(nice_title,logs_list[key]['id']); if(typeof body['persistent']['printer_log'] != 'undefined'){ if( body['persistent']['printer_log'] == logs_list[key]['id'] ){ - console.log("setting selected log: " + logs_list[key]['id']); + //console.log("setting selected log: " + logs_list[key]['id']); new_option.selected = true; } } @@ -1183,7 +1200,7 @@ log_to_print_dropdown.options[property_dropdown.options.length] = new Option("value", "log name"); } */ - console.log( document.getElementById('extension-privacy-manager-printer-log-dropdown-container') ); + //console.log( document.getElementById('extension-privacy-manager-printer-log-dropdown-container') ); document.getElementById('extension-privacy-manager-printer-log-dropdown-container').innerHTML = ""; document.getElementById('extension-privacy-manager-printer-log-dropdown-container').append(log_to_print_dropdown); @@ -1212,7 +1229,7 @@ // Upgrade thing title and append property title human_readable_thing_title = this.thing_title_lookup_table[human_readable_thing_title].title + ' - ' + human_readable_property_title; } - console.log("original title after lookup: ", human_readable_thing_title); + //console.log("original title after lookup: ", human_readable_thing_title); return human_readable_thing_title; //console.log(human_readable_thing_title); } diff --git a/manifest.json b/manifest.json index beb445b..b087139 100644 --- a/manifest.json +++ b/manifest.json @@ -41,7 +41,7 @@ } }, "short_name": "Privacy", - "version": "0.1.10", + "version": "0.1.9", "web_accessible_resources": [ "css/*.css", "images/*.svg", diff --git a/pkg/privacy_manager.py b/pkg/privacy_manager.py index 8078768..f1fb412 100644 --- a/pkg/privacy_manager.py +++ b/pkg/privacy_manager.py @@ -68,9 +68,19 @@ def __init__(self, verbose=False): self.things = [] # Holds all the things, updated via the API. Used to display a nicer thing name instead of the technical internal ID. self.data_types_lookup_table = {} - + self.duration_lookup_table = {'1':'1 minute', + '10':'10 minutes', + '20':'20 minutes', + '60':'1 hour', + '120':'2 hours', + '240':'4 hours', + '480':'8 hours', + } + + # printer self.doing_bluetooth_scan = False self.printer = None + self.busy_connecting_to_printer = False self.printer_connected = False self.printer_connection_counter = 0 self.printer_contrast = 1 @@ -78,19 +88,11 @@ def __init__(self, verbose=False): self.should_print_log_name = False self.date_string_to_print = "" self.power_timeout_set = False + self.last_printer_check_time = 0 + self.printer_disconnected_counter = 0 + self.printer_disconnected_retry_delay = 30 # can be as low as 30 seconds initially, then slowly grows to about 10 minutes between reconnect attempts - self.duration_lookup_table = {'1':'1 minute', - '10':'10 minutes', - '20':'20 minutes', - '60':'1 hour', - '120':'2 hours', - '240':'4 hours', - '480':'8 hours', - } - - - #print(str( time(11, 59, 59) )) try: manifest_fname = os.path.join( @@ -143,6 +145,7 @@ def __init__(self, verbose=False): self.persistent_data = {'printer_mac':'', 'printer_name':'','internal_logs_auto_delete':True} + should_save_persistent_data = False # Get persistent data try: with open(self.persistence_file_path) as f: @@ -151,29 +154,38 @@ def __init__(self, verbose=False): if self.DEBUG: print("Persistence data was loaded succesfully.") - if 'printer_mac' not in self.persistent_data: - if self.DEBUG: - print("printer_mac was not in persistent data, adding it now.") - self.persistent_data['printer_mac'] = '' - self.persistent_data['printer_name'] = '' - self.save_persistent_data() - - if 'internal_logs_auto_delete' not in self.persistent_data: - self.persistent_data['internal_logs_auto_delete'] = True + except: print("Could not load persistent data (if you just installed the add-on then this is normal)") - self.save_persistent_data() + self.persistent_data = {'printer_mac':'', 'printer_name':'','internal_logs_auto_delete':True} + should_save_persistent_data = True + + if 'printer_mac' not in self.persistent_data: + if self.DEBUG: + print("printer_mac was not in persistent data, adding it now.") + self.persistent_data['printer_mac'] = '' + self.persistent_data['printer_name'] = '' + should_save_persistent_data = True + + if 'internal_logs_auto_delete' not in self.persistent_data: + self.persistent_data['internal_logs_auto_delete'] = True + should_save_persistent_data = True if 'duration' not in self.persistent_data: self.persistent_data['duration'] = 30 + should_save_persistent_data = True if 'printer_log_name' not in self.persistent_data: self.persistent_data['printer_log_name'] = "" + should_save_persistent_data = True if 'printer_contrast' not in self.persistent_data: self.persistent_data['printer_contrast'] = 'medium' + should_save_persistent_data = True + if should_save_persistent_data: + self.save_persistent_data() self.get_logs_list() @@ -224,7 +236,7 @@ def clock(self): #last_run = 0 previous_scheduled_print_time = 0 #previous_printer_battery_check_time = 0 - self.printer_connection_counter = 0 + #self.printer_connection_counter = 0 while self.running: #last_run = time_module.time() @@ -313,30 +325,45 @@ def clock(self): time_module.sleep(1) # Keep bluetooth connection to printer alive - try: - if self.printer != None: - if self.printer_connected: - self.printer_connection_counter += 1 - #if timestamp - previous_printer_battery_check_time > 30: - if self.printer_connection_counter > 28: - if self.DEBUG: - print("requesting battery level from printer to keep it awake") - self.printer_connection_counter = 0 - self.printer_battery_level = self.printer.getDeviceBattery() - try: - if 'printer_battery' in self.adapter.thing.properties: - self.adapter.thing.properties['printer_battery'].update(int(self.printer_battery_level)) - except Exception as ex: - print("error updating battery level on thing: " + str(ex)) - + if not self.busy_connecting_to_printer: + try: + if self.printer != None: + if self.printer_connected: + self.printer_connection_counter += 1 + self.printer_disconnected_retry_delay = 30 + #if timestamp - previous_printer_battery_check_time > 30: + if self.printer_connection_counter > 28: + if self.DEBUG: + print("requesting battery level from printer to keep it awake") + self.printer_connection_counter = 0 + self.printer_battery_level = self.printer.getDeviceBattery() + if self.DEBUG: + print("self.printer_battery_level: " + str(self.printer_battery_level)) + try: + if 'printer_battery' in self.adapter.thing.properties: + self.adapter.thing.properties['printer_battery'].update(int(self.printer_battery_level)) + except Exception as ex: + print("error updating battery level on thing: " + str(ex)) + else: + self.printer_disconnected_counter += 1 + if self.printer_disconnected_counter > self.printer_disconnected_retry_delay: + self.printer_disconnected_counter = 0 + if self.print_test(): + if self.DEBUG: + print("Clock: succesfully reconnected to printer") + else: + if self.printer_disconnected_retry_delay < 600: + self.printer_disconnected_retry_delay += 60 + if self.DEBUG: + print("Clock: could not reconnect to printer") #if self.DEBUG: # print(f'Printer battery level: {self.printer_battery_level}%') - except Exception as ex: - if self.DEBUG: - print("Error with periodic connnection upkeep to printer: " + str(ex)) - self.printer_connected = False + except Exception as ex: + if self.DEBUG: + print("Error with periodic connnection upkeep to printer: " + str(ex)) + #self.printer_connected = False # print any picture that appears @@ -409,7 +436,7 @@ def handle_request(self, request): print("warning, Privacy Manager API received a GET request") return APIResponse(status=404) - if request.path == '/ajax' or request.path == '/get_property_data' or request.path == '/point_change_value' or request.path == '/point_delete' or request.path == '/internal_logs' or request.path == '/init' or request.path == '/sculptor_init' or request.path == '/printer_init' or request.path == '/printer_scan' or request.path == '/printer_set' or request.path == '/print_now' or request.path == '/print_test': + if request.path == '/ajax' or request.path == '/get_property_data' or request.path == '/point_change_value' or request.path == '/point_delete' or request.path == '/internal_logs' or request.path == '/init' or request.path == '/sculptor_init' or request.path == '/printer_init' or request.path == '/printer_scan' or request.path == '/printer_set' or request.path == '/print_now' or request.path == '/print_test' or request.path == '/print_image': try: if request.path == '/ajax': @@ -595,6 +622,41 @@ def handle_request(self, request): ) + elif request.path == '/print_image': + try: + state = 'ok' + if self.DEBUG: + print("REQUEST TO PRINT ICON") + + if 'filename' in request.body: + filename = request.body['filename'] + icon_path = os.path.join(self.addon_path, 'images', filename) + if os.path.isfile(icon_path) and os.path.isdir(self.external_picture_drop_dir): + destination_path = os.path.join(self.external_picture_drop_dir, filename) + copy_command = 'cp ' + str(icon_path) + ' ' + destination_path + if self.DEBUG: + print("copying icon to drop-off dir. Copy command: " + str(copy_command)) + os.system(copy_command) + else: + state = 'error' + else: + state = 'error' + + return APIResponse( + status=200, + content_type='application/json', + content=json.dumps({'state' : state}), + ) + except Exception as ex: + print("Error in /print_now: " + str(ex)) + return APIResponse( + status=500, + content_type='application/json', + content=json.dumps({'state': 'error', 'message':'Error while doing test print: ' + str(ex)}), + ) + + + elif request.path == '/get_property_data': @@ -1147,6 +1209,8 @@ def scan_bluetooth(self): result = False try: command = "sudo hcitool scan" + # TODO: add timeout? Switch to bluetoothctl? + #sudo timeout -s SIGINT 5s hcitool -i hci0 lescan > file.txt for line in self.run_command_with_lines(command): line = line.lower().strip() @@ -1653,6 +1717,13 @@ def print_now(self): def connect_to_printer(self): if self.DEBUG: print("in connect_to_printer") + + self.busy_connecting_to_printer = True + if time_module.time() - self.last_printer_check_time < 5: + if self.DEBUG: + print("connected check was already recently performed, skipping doing it again.") + return self.printer_connected + try: if self.printer == None and self.persistent_data['printer_mac'] != '': if self.DEBUG: @@ -1664,7 +1735,10 @@ def connect_to_printer(self): if self.printer != None: if self.DEBUG: print("printer object exists") - if self.printer.isConnected() == False: + self.printer_connected = self.printer.isConnected() + if self.DEBUG: + print("self.printer_connected: " + str(self.printer_connected)) + if self.printer_connected == False: if self.DEBUG: print("Printer is not connected. Will attempt to reconnect.") try: @@ -1680,26 +1754,44 @@ def connect_to_printer(self): if self.DEBUG: print("printer.reconnect also didn't work. Error: " + str(ex)) - if self.printer.isConnected(): - if self.power_timeout_set == False: - self.power_timeout_set = True - self.printer.setPowerTimeout(300) # minutes. Tells device to stay awake. 300 minutes = 5 hours - os.system('sudo bluetoothctl trust ' + str(self.persistent_data['printer_mac'])) - + self.last_printer_check_time = time_module.time() - self.printer_connected = True + + if self.printer_connected == False: + time_module.sleep(1) + self.printer_connected = self.printer.isConnected() + + if self.printer_connected: self.printer_connection_counter = 0 + + if self.power_timeout_set == False: + self.power_timeout_set = True + try: + self.printer.setPowerTimeout(300) # minutes. Tells device to stay awake. 300 minutes = 5 hours + os.system('sudo bluetoothctl trust ' + str(self.persistent_data['printer_mac'])) + except Exception as ex: + if self.DEBUG: + print("setPowerTimeout error: " + str(ex)) + + + if self.DEBUG: - print(f'Name: {self.printer.getDeviceName()}') - print(f'S/N: {self.printer.getDeviceSerialNumber()}') - print(f'F/W: {self.printer.getDeviceFirmware()}') - print(f'Battery: {self.printer.getDeviceBattery()}%') - print(f'H/W: {self.printer.getDeviceHardware()}') - print(f'MAC: {self.printer.getDeviceMAC()}') + try: + print(f'Name: {self.printer.getDeviceName()}') + print(f'S/N: {self.printer.getDeviceSerialNumber()}') + print(f'F/W: {self.printer.getDeviceFirmware()}') + print(f'Battery: {self.printer.getDeviceBattery()}%') + print(f'H/W: {self.printer.getDeviceHardware()}') + print(f'MAC: {self.printer.getDeviceMAC()}') + except Exception as ex: + if self.DEBUG: + print("Debug only Error while asking the printer for details: " + str(ex)) + else: - print("ERROR, was unable to (re)connect to the printer") - self.printer_connected = False + if self.DEBUG: + print("ERROR, was unable to (re)connect to the printer") + # update thing property if self.adapter != None: if 'printer_connected' in self.adapter.thing.properties: self.adapter.thing.properties['printer_connected'].update(self.printer_connected) @@ -1710,8 +1802,11 @@ def connect_to_printer(self): except Exception as ex: print("error setting up printer: " + str(ex)) + self.printer_connected = False + self.busy_connecting_to_printer = False + return self.printer_connected def print_image_file(self,filename, print_rotation=0): diff --git a/views/content.html b/views/content.html index 58fcf42..85fed9d 100644 --- a/views/content.html +++ b/views/content.html @@ -321,7 +321,7 @@
If you connect a PeriPage Bluetooth printer, then you can print your log data to paper at regular intervals. For example, each day at midnight you could print all the data that was collected during the day. After the print is complete, the log's data will be deleted from the system. So at the end of the day, the paper print will be the only copy of your data that remains.
This is an interesting option to have for very sensitive data. For example, you could print data about your health, and be sure that no digital copy remains.
@@ -386,17 +386,34 @@