extends Control var GodotBluetooth344 var location_permission var location_status var bluetooth_status var connected var devices = [] var item_selected var services = [] var item_service_selected # Test UUIDs export var service_uuid = "6e400001-b5a3-f393-e0a9-e50e24dcca9e" export var read_uuid = "6e400003-b5a3-f393-e0a9-e50e24dcca9e" export var write_uuid = "6e400002-b5a3-f393-e0a9-e50e24dcca9e" onready var devices_list = $VBoxContainer/Devices/FoundDevicesList onready var services_list = $VBoxContainer/Services/FoundServiceList onready var log_node = $VBoxContainer/Log/Log onready var send_line_edit = $VBoxContainer/TextAndButtons/HBoxContainer/SendTextLine onready var send_button = $VBoxContainer/TextAndButtons/HBoxContainer/SendTextButton onready var scan_button = $VBoxContainer/TextAndButtons/Buttons/ScanButton onready var connect_button = $VBoxContainer/TextAndButtons/Buttons/ConnectButton onready var disconnect_button = $VBoxContainer/TextAndButtons/Buttons/DisconnectButton # UI helpers onready var bluetooth_label = $VBoxContainer/TopLabels/HBoxContainer2/HBoxContainer/BluetoohStatusLabel2 onready var location_label = $VBoxContainer/TopLabels/VBoxContainer/HBoxContainer3/LocationStatusLabel2 onready var location_permission_label = $VBoxContainer/TopLabels/VBoxContainer/HBoxContainer/LocationPermissionLabel2 onready var connected_label = $VBoxContainer/TopLabels/HBoxContainer2/HBoxContainer2/ConnectedLabel2 #onready var devices_list = $UserInterfaceCanvasLayer/VBoxContainer/Devices/FoundDevicesList #onready var services_list = $UserInterfaceCanvasLayer/VBoxContainer/Services/FoundServiceList #onready var log_node = $UserInterfaceCanvasLayer/VBoxContainer/Log/Log #onready var send_line_edit = $UserInterfaceCanvasLayer/VBoxContainer/TextAndButtons/HBoxContainer/SendTextLine # #onready var send_button = $UserInterfaceCanvasLayer/VBoxContainer/TextAndButtons/HBoxContainer/SendTextButton #onready var scan_button = $UserInterfaceCanvasLayer/VBoxContainer/TextAndButtons/Buttons/ScanButton #onready var connect_button = $UserInterfaceCanvasLayer/VBoxContainer/TextAndButtons/Buttons/ConnectButton #onready var disconnect_button = $UserInterfaceCanvasLayer/VBoxContainer/TextAndButtons/Buttons/DisconnectButton # ## UI helpers #onready var bluetooth_label = $UserInterfaceCanvasLayer/VBoxContainer/TopLabels/HBoxContainer2/HBoxContainer/BluetoohStatusLabel2 #onready var location_label = $UserInterfaceCanvasLayer/VBoxContainer/TopLabels/VBoxContainer/HBoxContainer3/LocationStatusLabel2 #onready var location_permission_label = $UserInterfaceCanvasLayer/VBoxContainer/TopLabels/VBoxContainer/HBoxContainer/LocationPermissionLabel2 #onready var connected_label = $UserInterfaceCanvasLayer/VBoxContainer/TopLabels/HBoxContainer2/HBoxContainer2/ConnectedLabel2 var DEBUG_WINDOWS = false var APPLICATION_TITLE var VERSION = "1.1" var USERABSOLUTEID_HW var networkDB_node # BLUETTI TESTING: # Plugin code https://github.com/pablojimenezmateo/GodotAndroidBluetoothPlugin # Check mqtt sources https://pypi.org/project/bluetti-mqtt/#files # DELETE FROM `SWD_MSGBUS` WHERE LABEL <> 'AC200M2236006098676' var device_names_BLUETTI = ['AC200M', 'AC300', 'AC500', 'EP500', 'EP500P', 'EB3A'] var write_uuid_BLUETTI = '0000ff02-0000-1000-8000-00805f9b34fb' var notify_uuid_BLUETTI = '0000ff01-0000-1000-8000-00805f9b34fb' # BLUETOOTH: var BLUETOOTH_CODES = { notify_uuid_BLUETTI: "Bluetti notify", write_uuid_BLUETTI: "Bluetti write", 'f7bf3564-fb6d-4e53-88a4-5e37e0326063': "OTA Control Characteristic", '984227f3-34fc-4045-a5d0-2c581f81a153': "OTA Data Characteristic", '00002a00-0000-1000-8000-00805f9b34fb': 'Device Name', '00002a01-0000-1000-8000-00805f9b34fb': 'Appearance', '00002a02-0000-1000-8000-00805f9b34fb': 'Peripheral Privacy Flag', '00002a03-0000-1000-8000-00805f9b34fb': 'Reconnection Address', '00002a04-0000-1000-8000-00805f9b34fb': 'Peripheral Preferred Connection Parameters', '00002a05-0000-1000-8000-00805f9b34fb': 'Service Changed', '00002a06-0000-1000-8000-00805f9b34fb': 'Alert Level', '00002a07-0000-1000-8000-00805f9b34fb': 'Tx Power Level', '00002a08-0000-1000-8000-00805f9b34fb': 'Date Time', '00002a09-0000-1000-8000-00805f9b34fb': 'Day of Week', '00002a0a-0000-1000-8000-00805f9b34fb': 'Day Date Time', '00002a0b-0000-1000-8000-00805f9b34fb': 'Exact Time 100', '00002a0c-0000-1000-8000-00805f9b34fb': 'Exact Time 256', '00002a0d-0000-1000-8000-00805f9b34fb': 'DST Offset', '00002a0e-0000-1000-8000-00805f9b34fb': 'Time Zone', '00002a0f-0000-1000-8000-00805f9b34fb': 'Local Time Information', '00002a10-0000-1000-8000-00805f9b34fb': 'Secondary Time Zone', '00002a11-0000-1000-8000-00805f9b34fb': 'Time with DST', '00002a12-0000-1000-8000-00805f9b34fb': 'Time Accuracy', '00002a13-0000-1000-8000-00805f9b34fb': 'Time Source', '00002a14-0000-1000-8000-00805f9b34fb': 'Reference Time Information', '00002a15-0000-1000-8000-00805f9b34fb': 'Time Broadcast', '00002a16-0000-1000-8000-00805f9b34fb': 'Time Update Control Point', '00002a17-0000-1000-8000-00805f9b34fb': 'Time Update State', '00002a18-0000-1000-8000-00805f9b34fb': 'Glucose Measurement', '00002a19-0000-1000-8000-00805f9b34fb': 'Battery Level', '00002a1a-0000-1000-8000-00805f9b34fb': 'Battery Power State', '00002a1b-0000-1000-8000-00805f9b34fb': 'Battery Level State', '00002a1c-0000-1000-8000-00805f9b34fb': 'Temperature Measurement', '00002a1d-0000-1000-8000-00805f9b34fb': 'Temperature Type', '00002a1e-0000-1000-8000-00805f9b34fb': 'Intermediate Temperature', '00002a1f-0000-1000-8000-00805f9b34fb': 'Temperature Celsius', '00002a20-0000-1000-8000-00805f9b34fb': 'Temperature Fahrenheit', '00002a21-0000-1000-8000-00805f9b34fb': 'Measurement Interval', '00002a22-0000-1000-8000-00805f9b34fb': 'Boot Keyboard Input Report', '00002a23-0000-1000-8000-00805f9b34fb': 'System ID', '00002a24-0000-1000-8000-00805f9b34fb': 'Model Number String', '00002a25-0000-1000-8000-00805f9b34fb': 'Serial Number String', '00002a26-0000-1000-8000-00805f9b34fb': 'Firmware Revision String', '00002a27-0000-1000-8000-00805f9b34fb': 'Hardware Revision String', '00002a28-0000-1000-8000-00805f9b34fb': 'Software Revision String', '00002a29-0000-1000-8000-00805f9b34fb': 'Manufacturer Name String', '00002a2a-0000-1000-8000-00805f9b34fb': 'IEEE 11073-20601 Regulatory Certification Data List', '00002a2b-0000-1000-8000-00805f9b34fb': 'Current Time', '00002a2c-0000-1000-8000-00805f9b34fb': 'Magnetic Declination', '00002a2f-0000-1000-8000-00805f9b34fb': 'Position 2D', '00002a30-0000-1000-8000-00805f9b34fb': 'Position 3D', '00002a31-0000-1000-8000-00805f9b34fb': 'Scan Refresh', '00002a32-0000-1000-8000-00805f9b34fb': 'Boot Keyboard Output Report', '00002a33-0000-1000-8000-00805f9b34fb': 'Boot Mouse Input Report', '00002a34-0000-1000-8000-00805f9b34fb': 'Glucose Measurement Context', '00002a35-0000-1000-8000-00805f9b34fb': 'Blood Pressure Measurement', '00002a36-0000-1000-8000-00805f9b34fb': 'Intermediate Cuff Pressure', '00002a37-0000-1000-8000-00805f9b34fb': 'Heart Rate Measurement', '00002a38-0000-1000-8000-00805f9b34fb': 'Body Sensor Location', '00002a39-0000-1000-8000-00805f9b34fb': 'Heart Rate Control Point', '00002a3a-0000-1000-8000-00805f9b34fb': 'Removable', '00002a3b-0000-1000-8000-00805f9b34fb': 'Service Required', '00002a3c-0000-1000-8000-00805f9b34fb': 'Scientific Temperature Celsius', '00002a3d-0000-1000-8000-00805f9b34fb': 'String', '00002a3e-0000-1000-8000-00805f9b34fb': 'Network Availability', '00002a3f-0000-1000-8000-00805f9b34fb': 'Alert Status', '00002a40-0000-1000-8000-00805f9b34fb': 'Ringer Control point', '00002a41-0000-1000-8000-00805f9b34fb': 'Ringer Setting', '00002a42-0000-1000-8000-00805f9b34fb': 'Alert Category ID Bit Mask', '00002a43-0000-1000-8000-00805f9b34fb': 'Alert Category ID', '00002a44-0000-1000-8000-00805f9b34fb': 'Alert Notification Control Point', '00002a45-0000-1000-8000-00805f9b34fb': 'Unread Alert Status', '00002a46-0000-1000-8000-00805f9b34fb': 'New Alert', '00002a47-0000-1000-8000-00805f9b34fb': 'Supported New Alert Category', '00002a48-0000-1000-8000-00805f9b34fb': 'Supported Unread Alert Category', '00002a49-0000-1000-8000-00805f9b34fb': 'Blood Pressure Feature', '00002a4a-0000-1000-8000-00805f9b34fb': 'HID Information', '00002a4b-0000-1000-8000-00805f9b34fb': 'Report Map', '00002a4c-0000-1000-8000-00805f9b34fb': 'HID Control Point', '00002a4d-0000-1000-8000-00805f9b34fb': 'Report', '00002a4e-0000-1000-8000-00805f9b34fb': 'Protocol Mode', '00002a4f-0000-1000-8000-00805f9b34fb': 'Scan Interval Window', '00002a50-0000-1000-8000-00805f9b34fb': 'PnP ID', '00002a51-0000-1000-8000-00805f9b34fb': 'Glucose Feature', '00002a52-0000-1000-8000-00805f9b34fb': 'Record Access Control Point', '00002a53-0000-1000-8000-00805f9b34fb': 'RSC Measurement', '00002a54-0000-1000-8000-00805f9b34fb': 'RSC Feature', '00002a55-0000-1000-8000-00805f9b34fb': 'SC Control Point', '00002a56-0000-1000-8000-00805f9b34fb': 'Digital', '00002a57-0000-1000-8000-00805f9b34fb': 'Digital Output', '00002a58-0000-1000-8000-00805f9b34fb': 'Analog', '00002a59-0000-1000-8000-00805f9b34fb': 'Analog Output', '00002a5a-0000-1000-8000-00805f9b34fb': 'Aggregate', '00002a5b-0000-1000-8000-00805f9b34fb': 'CSC Measurement', '00002a5c-0000-1000-8000-00805f9b34fb': 'CSC Feature', '00002a5d-0000-1000-8000-00805f9b34fb': 'Sensor Location', '00002a5e-0000-1000-8000-00805f9b34fb': 'PLX Spot-Check Measurement', '00002a5f-0000-1000-8000-00805f9b34fb': 'PLX Continuous Measurement Characteristic', '00002a60-0000-1000-8000-00805f9b34fb': 'PLX Features', '00002a62-0000-1000-8000-00805f9b34fb': 'Pulse Oximetry Control Point', '00002a63-0000-1000-8000-00805f9b34fb': 'Cycling Power Measurement', '00002a64-0000-1000-8000-00805f9b34fb': 'Cycling Power Vector', '00002a65-0000-1000-8000-00805f9b34fb': 'Cycling Power Feature', '00002a66-0000-1000-8000-00805f9b34fb': 'Cycling Power Control Point', '00002a67-0000-1000-8000-00805f9b34fb': 'Location and Speed Characteristic', '00002a68-0000-1000-8000-00805f9b34fb': 'Navigation', '00002a69-0000-1000-8000-00805f9b34fb': 'Position Quality', '00002a6a-0000-1000-8000-00805f9b34fb': 'LN Feature', '00002a6b-0000-1000-8000-00805f9b34fb': 'LN Control Point', '00002a6c-0000-1000-8000-00805f9b34fb': 'Elevation', '00002a6d-0000-1000-8000-00805f9b34fb': 'Pressure', '00002a6e-0000-1000-8000-00805f9b34fb': 'Temperature', '00002a6f-0000-1000-8000-00805f9b34fb': 'Humidity', '00002a70-0000-1000-8000-00805f9b34fb': 'True Wind Speed', '00002a71-0000-1000-8000-00805f9b34fb': 'True Wind Direction', '00002a72-0000-1000-8000-00805f9b34fb': 'Apparent Wind Speed', '00002a73-0000-1000-8000-00805f9b34fb': 'Apparent Wind Direction', '00002a74-0000-1000-8000-00805f9b34fb': 'Gust Factor', '00002a75-0000-1000-8000-00805f9b34fb': 'Pollen Concentration', '00002a76-0000-1000-8000-00805f9b34fb': 'UV Index', '00002a77-0000-1000-8000-00805f9b34fb': 'Irradiance', '00002a78-0000-1000-8000-00805f9b34fb': 'Rainfall', '00002a79-0000-1000-8000-00805f9b34fb': 'Wind Chill', '00002a7a-0000-1000-8000-00805f9b34fb': 'Heat Index', '00002a7b-0000-1000-8000-00805f9b34fb': 'Dew Point', '00002a7d-0000-1000-8000-00805f9b34fb': 'Descriptor Value Changed', '00002a7e-0000-1000-8000-00805f9b34fb': 'Aerobic Heart Rate Lower Limit', '00002a7f-0000-1000-8000-00805f9b34fb': 'Aerobic Threshold', '00002a80-0000-1000-8000-00805f9b34fb': 'Age', '00002a81-0000-1000-8000-00805f9b34fb': 'Anaerobic Heart Rate Lower Limit', '00002a82-0000-1000-8000-00805f9b34fb': 'Anaerobic Heart Rate Upper Limit', '00002a83-0000-1000-8000-00805f9b34fb': 'Anaerobic Threshold', '00002a84-0000-1000-8000-00805f9b34fb': 'Aerobic Heart Rate Upper Limit', '00002a85-0000-1000-8000-00805f9b34fb': 'Date of Birth', '00002a86-0000-1000-8000-00805f9b34fb': 'Date of Threshold Assessment', '00002a87-0000-1000-8000-00805f9b34fb': 'Email Address', '00002a88-0000-1000-8000-00805f9b34fb': 'Fat Burn Heart Rate Lower Limit', '00002a89-0000-1000-8000-00805f9b34fb': 'Fat Burn Heart Rate Upper Limit', '00002a8a-0000-1000-8000-00805f9b34fb': 'First Name', '00002a8b-0000-1000-8000-00805f9b34fb': 'Five Zone Heart Rate Limits', '00002a8c-0000-1000-8000-00805f9b34fb': 'Gender', '00002a8d-0000-1000-8000-00805f9b34fb': 'Heart Rate Max', '00002a8e-0000-1000-8000-00805f9b34fb': 'Height', '00002a8f-0000-1000-8000-00805f9b34fb': 'Hip Circumference', '00002a90-0000-1000-8000-00805f9b34fb': 'Last Name', '00002a91-0000-1000-8000-00805f9b34fb': 'Maximum Recommended Heart Rate', '00002a92-0000-1000-8000-00805f9b34fb': 'Resting Heart Rate', '00002a93-0000-1000-8000-00805f9b34fb': 'Sport Type for Aerobic and Anaerobic Thresholds', '00002a94-0000-1000-8000-00805f9b34fb': 'Three Zone Heart Rate Limits', '00002a95-0000-1000-8000-00805f9b34fb': 'Two Zone Heart Rate Limit', '00002a96-0000-1000-8000-00805f9b34fb': 'VO2 Max', '00002a97-0000-1000-8000-00805f9b34fb': 'Waist Circumference', '00002a98-0000-1000-8000-00805f9b34fb': 'Weight', '00002a99-0000-1000-8000-00805f9b34fb': 'Database Change Increment', '00002a9a-0000-1000-8000-00805f9b34fb': 'User Index', '00002a9b-0000-1000-8000-00805f9b34fb': 'Body Composition Feature', '00002a9c-0000-1000-8000-00805f9b34fb': 'Body Composition Measurement', '00002a9d-0000-1000-8000-00805f9b34fb': 'Weight Measurement', '00002a9e-0000-1000-8000-00805f9b34fb': 'Weight Scale Feature', '00002a9f-0000-1000-8000-00805f9b34fb': 'User Control Point', '00002aa0-0000-1000-8000-00805f9b34fb': 'Magnetic Flux Density - 2D', '00002aa1-0000-1000-8000-00805f9b34fb': 'Magnetic Flux Density - 3D', '00002aa2-0000-1000-8000-00805f9b34fb': 'Language', '00002aa3-0000-1000-8000-00805f9b34fb': 'Barometric Pressure Trend', '00002aa4-0000-1000-8000-00805f9b34fb': 'Bond Management Control Point', '00002aa5-0000-1000-8000-00805f9b34fb': 'Bond Management Features', '00002aa6-0000-1000-8000-00805f9b34fb': 'Central Address Resolution', '00002aa7-0000-1000-8000-00805f9b34fb': 'CGM Measurement', '00002aa8-0000-1000-8000-00805f9b34fb': 'CGM Feature', '00002aa9-0000-1000-8000-00805f9b34fb': 'CGM Status', '00002aaa-0000-1000-8000-00805f9b34fb': 'CGM Session Start Time', '00002aab-0000-1000-8000-00805f9b34fb': 'CGM Session Run Time', '00002aac-0000-1000-8000-00805f9b34fb': 'CGM Specific Ops Control Point', '00002aad-0000-1000-8000-00805f9b34fb': 'Indoor Positioning Configuration', '00002aae-0000-1000-8000-00805f9b34fb': 'Latitude', '00002aaf-0000-1000-8000-00805f9b34fb': 'Longitude', '00002ab0-0000-1000-8000-00805f9b34fb': 'Local North Coordinate', '00002ab1-0000-1000-8000-00805f9b34fb': 'Local East Coordinate', '00002ab2-0000-1000-8000-00805f9b34fb': 'Floor Number', '00002ab3-0000-1000-8000-00805f9b34fb': 'Altitude', '00002ab4-0000-1000-8000-00805f9b34fb': 'Uncertainty', '00002ab5-0000-1000-8000-00805f9b34fb': 'Location Name', '00002ab6-0000-1000-8000-00805f9b34fb': 'URI', '00002ab7-0000-1000-8000-00805f9b34fb': 'HTTP Headers', '00002ab8-0000-1000-8000-00805f9b34fb': 'HTTP Status Code', '00002ab9-0000-1000-8000-00805f9b34fb': 'HTTP Entity Body', '00002aba-0000-1000-8000-00805f9b34fb': 'HTTP Control Point', '00002abb-0000-1000-8000-00805f9b34fb': 'HTTPS Security', '00002abc-0000-1000-8000-00805f9b34fb': 'TDS Control Point', '00002abd-0000-1000-8000-00805f9b34fb': 'OTS Feature', '00002abe-0000-1000-8000-00805f9b34fb': 'Object Name', '00002abf-0000-1000-8000-00805f9b34fb': 'Object Type', '00002ac0-0000-1000-8000-00805f9b34fb': 'Object Size', '00002ac1-0000-1000-8000-00805f9b34fb': 'Object First-Created', '00002ac2-0000-1000-8000-00805f9b34fb': 'Object Last-Modified', '00002ac3-0000-1000-8000-00805f9b34fb': 'Object ID', '00002ac4-0000-1000-8000-00805f9b34fb': 'Object Properties', '00002ac5-0000-1000-8000-00805f9b34fb': 'Object Action Control Point', '00002ac6-0000-1000-8000-00805f9b34fb': 'Object List Control Point', '00002ac7-0000-1000-8000-00805f9b34fb': 'Object List Filter', '00002ac8-0000-1000-8000-00805f9b34fb': 'Object Changed', '00002ac9-0000-1000-8000-00805f9b34fb': 'Resolvable Private Address Only', '00002acc-0000-1000-8000-00805f9b34fb': 'Fitness Machine Feature', '00002acd-0000-1000-8000-00805f9b34fb': 'Treadmill Data', '00002ace-0000-1000-8000-00805f9b34fb': 'Cross Trainer Data', '00002acf-0000-1000-8000-00805f9b34fb': 'Step Climber Data', '00002ad0-0000-1000-8000-00805f9b34fb': 'Stair Climber Data', '00002ad1-0000-1000-8000-00805f9b34fb': 'Rower Data', '00002ad2-0000-1000-8000-00805f9b34fb': 'Indoor Bike Data', '00002ad3-0000-1000-8000-00805f9b34fb': 'Training Status', '00002ad4-0000-1000-8000-00805f9b34fb': 'Supported Speed Range', '00002ad5-0000-1000-8000-00805f9b34fb': 'Supported Inclination Range', '00002ad6-0000-1000-8000-00805f9b34fb': 'Supported Resistance Level Range', '00002ad7-0000-1000-8000-00805f9b34fb': 'Supported Heart Rate Range', '00002ad8-0000-1000-8000-00805f9b34fb': 'Supported Power Range', '00002ad9-0000-1000-8000-00805f9b34fb': 'Fitness Machine Control Point', '00002ada-0000-1000-8000-00805f9b34fb': 'Fitness Machine Status', '00002aed-0000-1000-8000-00805f9b34fb': 'Date UTC', '00002b1d-0000-1000-8000-00805f9b34fb': 'RC Feature', '00002b1e-0000-1000-8000-00805f9b34fb': 'RC Settings', '00002b1f-0000-1000-8000-00805f9b34fb': 'Reconnection Configuration Control Point', '0000ff01-0000-1000-8000-00805f9b34fb': 'Device Info', '0000ff02-0000-1000-8000-00805f9b34fb': 'Device Name', '0000ff03-0000-1000-8000-00805f9b34fb': 'Notification', '0000ff04-0000-1000-8000-00805f9b34fb': 'User Info', '0000ff05-0000-1000-8000-00805f9b34fb': 'Control Point', '0000ff06-0000-1000-8000-00805f9b34fb': 'Realtime Steps', '0000ff07-0000-1000-8000-00805f9b34fb': 'Activity Data', '0000ff08-0000-1000-8000-00805f9b34fb': 'Firmware Data', '0000ff09-0000-1000-8000-00805f9b34fb': 'LE Params', '0000ff0a-0000-1000-8000-00805f9b34fb': 'Date/Time', '0000ff0b-0000-1000-8000-00805f9b34fb': 'Statistics', '0000ff0c-0000-1000-8000-00805f9b34fb': 'Battery', '0000ff0d-0000-1000-8000-00805f9b34fb': 'Test', '0000ff0e-0000-1000-8000-00805f9b34fb': 'Sensor Data' } # Called when the node enters the scene tree for the first time. func _ready(): APPLICATION_TITLE = ProjectSettings.get_setting("application/config/name") USERABSOLUTEID_HW = OS.get_unique_id() log_string("[init] " + APPLICATION_TITLE + " v." + String(VERSION) + " on " + USERABSOLUTEID_HW) if (self.has_node("NetworkDB")): networkDB_node = self.get_node("NetworkDB") log_string("[init] " + "Network DB active on " + networkDB_node.internetSite) if (networkDB_node != null): var op = "LOGIN" var data = str(VERSION) var sql = "INSERT INTO SWD_MSGBUS (ID, CRT_DATE, APPLICATION, VERSION, OP, LABEL, DESCRIPTION, DEVICE_ID) VALUES (ID_INSERT,'TODAY','" + APPLICATION_TITLE + "','" + VERSION + "','" + op + "','','','" + USERABSOLUTEID_HW + "')" + ";" var args = ["UPDATE"] var res = networkDB_node.getExecSql(sql, args) if (res != null): log_string("[init] " + "Network DB Up and running") else: log_string("[init] " + "Network DB KO") if Engine.has_singleton("GodotBluetooth344"): GodotBluetooth344 = Engine.get_singleton("GodotBluetooth344") GodotBluetooth344.connect("_on_debug_message", self, "_on_debug_message") GodotBluetooth344.connect("_on_device_found", self, "_on_device_found") GodotBluetooth344.connect("_on_bluetooth_status_change", self, "_on_bluetooth_status_change") GodotBluetooth344.connect("_on_location_status_change", self, "_on_location_status_change") GodotBluetooth344.connect("_on_connection_status_change", self, "_on_connection_status_change") GodotBluetooth344.connect("_on_characteristic_found", self, "_on_characteristic_found") GodotBluetooth344.connect("_on_characteristic_finding", self, "_on_characteristic_finding") GodotBluetooth344.connect("_on_characteristic_read", self, "_on_characteristic_read") else: DEBUG_WINDOWS = true if (DEBUG_WINDOWS): bluetooth_status = true location_status = true location_permission = true connected = true set_location_permission() set_bluetooth_status() set_location_status() else: # Check the permissions check_permissions() connected = false set_connected() pass # ********************************************* # Scan\Discovery # ********************************************* func _on_device_found(new_device): # new_device is a dictionary with the following keys: # * address: MAC address of the device # * name: Name of the device # * rssi: Signal strength of the device in dBm log_string("Got a new device: " + str(new_device)) devices.append(new_device) devices_list.add_item(new_device.name + " " + new_device.address + " " + str(new_device.rssi)) if (networkDB_node != null): var op = "DISCOVERY" var data = str(new_device.name) + " " + str(new_device.address) + " " + str(new_device.rssi) var sql = "INSERT INTO SWD_MSGBUS (ID, CRT_DATE, APPLICATION, VERSION, OP, LABEL, DESCRIPTION, DEVICE_ID) VALUES (ID_INSERT,'TODAY','" + APPLICATION_TITLE + "','" + VERSION + "','" + op + "','" + new_device.name + "','" + data + "','" + USERABSOLUTEID_HW + "')" + ";" var args = ["UPDATE"] networkDB_node.getExecSql(sql, args) log_string("[_on_device_found] " + data) pass # ********************************************* # Services # ********************************************* func _on_characteristic_finding(status): # There can be 2 status: # * processing # * done log_string("[_on_characteristic_finding] " + str(status)) if status == "processing": services = [] services_list.clear() # elif status == "done": # GodotBluetooth344.subscribeToCharacteristic(service_uuid, read_uuid) if (DEBUG_WINDOWS): var characteristic = { "service_uuid": "XXXX", "characteristic_uuid":notify_uuid_BLUETTI, "real_mask":100, "readable":true, "writable":false, "writable_no_response":false } _on_characteristic_found(characteristic) var data = { "service_uuid": "XXXX", "characteristic_uuid":notify_uuid_BLUETTI, "bytes":PoolByteArray([0,1,0,1]) } _on_characteristic_read(data) pass func _on_characteristic_found(characteristic): # characteristic is a dictionary with the following keys # * service_uuid: The serice UUID # * characteristic_uuid: The characteristic UUID # * real_mask: The mask of the characteristic, for more information check: https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#getProperties() # * readable: If this characteristic is readable # * writable: If this characteristic is writable # * writable_no_response: If this characteristic is writable with no response log_string("[_on_characteristic_found] " + str(characteristic)) var knownServiceCharacteristic = "" if (BLUETOOTH_CODES.has(characteristic.characteristic_uuid)): knownServiceCharacteristic = BLUETOOTH_CODES[characteristic.characteristic_uuid] + " " var flags = "" if (characteristic.readable): flags = flags + "R" if (characteristic.writable): flags = flags + "W" if (characteristic.writable_no_response): flags = flags + "x" var data = knownServiceCharacteristic + str(characteristic.characteristic_uuid) + " [" + flags + "] (" + str(characteristic.service_uuid) + ") " services.append(characteristic) services_list.add_item(data) if (networkDB_node != null): var op = "SERVICE" var sql = "INSERT INTO SWD_MSGBUS (ID, CRT_DATE, APPLICATION, VERSION, OP, LABEL, DESCRIPTION, DEVICE_ID) VALUES (ID_INSERT,'TODAY','" + APPLICATION_TITLE + "','" + VERSION + "','" + op + "','" + devices[item_selected].name + "','" + data + "','" + USERABSOLUTEID_HW + "')" + ";" var args = ["UPDATE"] networkDB_node.getExecSql(sql, args) #log_string("[_on_characteristic_found] " + data) if (knownServiceCharacteristic != "") && (characteristic.readable): log_string("[subscribeToCharacteristic] " + knownServiceCharacteristic) if (! DEBUG_WINDOWS): GodotBluetooth344.subscribeToCharacteristic(characteristic.service_uuid, characteristic.characteristic_uuid) pass func _on_characteristic_read(data): # data is a dictionary with the following keys # * service_uuid: The serice UUID # * characteristic_uuid: The characteristic UUID # * bytes: They raw bytes of the payload log_string("[_on_characteristic_read] " + str(data.service_uuid) + "-" + str(data.characteristic_uuid)) log_string(str(data.bytes)) # If your bytes represent a UTF-8 string, use the # following code: var string = PoolByteArray(data.bytes).get_string_from_utf8() log_string(string) var knownServiceCharacteristic = "" if (BLUETOOTH_CODES.has(data.characteristic_uuid)): knownServiceCharacteristic = BLUETOOTH_CODES[data.characteristic_uuid] + " " if (networkDB_node != null): var op = "READ" var labelField = devices[item_selected].name + ":" + knownServiceCharacteristic + str(data.characteristic_uuid) + " (" + str(data.service_uuid) + ")" var dataStr = str(data.bytes) var sql = "INSERT INTO SWD_MSGBUS (ID, CRT_DATE, APPLICATION, VERSION, OP, LABEL, DESCRIPTION, DEVICE_ID) VALUES (ID_INSERT,'TODAY','" + APPLICATION_TITLE + "','" + VERSION + "','" + op + "','" + labelField + "','" + dataStr + "','" + USERABSOLUTEID_HW + "')" + ";" var args = ["UPDATE"] networkDB_node.getExecSql(sql, args) pass # ********************************************* # Status # ********************************************* func _on_connection_status_change(status): # There can be 3 status: # * connected # * disconnected # * An integer, this means an error, check https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#constants_2 for more information log_string("[_on_connection_status_change] " + status) # If you do not know the services and characteristics of the peer device # call listServicesAndCharacteristics() once connected if status == "connected": if (DEBUG_WINDOWS): _on_characteristic_finding("processing") else: GodotBluetooth344.listServicesAndCharacteristics() connected = true else: connected = false set_connected() pass func _on_location_status_change(status): # There can be 2 status: # * on # * off log_string("[_on_location_status_change] " + status) if status == "on": location_status = true else: location_status = false set_location_status() pass func _on_bluetooth_status_change(status): # There can be 4 status: # * on # * turning_on # * off # * turning_off log_string("[_on_bluetooth_status_change] " + status) if status == "on": bluetooth_status = true else: bluetooth_status = false set_bluetooth_status() pass func check_permissions(): var boolean log_string("Checking bluetooth status") boolean = GodotBluetooth344.bluetoothStatus() log_string(boolean) bluetooth_status = boolean log_string("Checking location status") boolean = GodotBluetooth344.locationStatus() log_string(boolean) location_status = boolean log_string("Checking location permissions") boolean = GodotBluetooth344.hasLocationPermissions() log_string(boolean) location_permission = boolean set_location_permission() set_bluetooth_status() set_location_status() set_location_permission() pass func _on_debug_message(s): print(s) log_string("[DEBUG] " + s) pass # ********************************************* # UI Events # ********************************************* func _on_ScanButton_button_up(): # Erase the results from the previous scan devices = [] devices_list.clear() item_selected = null set_buttons() if (DEBUG_WINDOWS): var new_device = { "name": device_names_BLUETTI[0], "address":"XXXX", "rssi":100 } _on_device_found(new_device) else: GodotBluetooth344.scan() pass func _on_ConnectButton_button_up(): if item_selected != null: var address = devices[item_selected].address log_string("Connecting to " + address) if (DEBUG_WINDOWS): _on_connection_status_change("connected") else: GodotBluetooth344.connect(address) pass func _on_DisconnectButton_button_up(): GodotBluetooth344.disconnect() pass # When a device on the list is selected, save the index func _on_FoundDevicesList_item_selected(index): item_selected = index set_buttons() pass func _on_FoundServiceList_item_selected(index): item_service_selected = index pass func _on_SendTextButton_button_up(): var text = send_line_edit.text if (item_service_selected != null) && (text != ""): GodotBluetooth344.writeStringToCharacteristic(item_service_selected.service_uuid, write_uuid, text) pass func set_bluetooth_status(): if bluetooth_status: bluetooth_label.text = "On" else: bluetooth_label.text = "Off" set_buttons() pass func set_location_status(): if location_status: location_label.text = "On" else: location_label.text = "Off" set_buttons() pass func set_location_permission(): if location_permission: location_permission_label.text = "Yes" else: location_permission_label.text = "No" set_buttons() pass func set_connected(): if connected: connected_label.text = "Yes" else: connected_label.text = "No" set_buttons() pass func set_buttons(): send_button.disabled = true scan_button.disabled = true connect_button.disabled = true disconnect_button.disabled = true # We need all 3 things to be able to scan if bluetooth_status and location_status and location_permission and not connected: # It is not necessary to disable the button when connected, # we can scan while connected but shows the workflow more clear # for the demo scan_button.disabled = false # If we have selected a device, enable the connect button if bluetooth_status and location_status and location_permission and item_selected != null and not connected: connect_button.disabled = false if bluetooth_status and location_status and location_permission and connected: send_button.disabled = false disconnect_button.disabled = false pass # Prints and writes to the log func log_string(s): var time = OS.get_time() var time_str = String(time.hour) +":"+String(time.minute)+":"+String(time.second) var text = "[" + time_str + "] " + str(s) log_node.add_text(text + "\n") print(text) pass