kind3r / ttlock-sdk-js

JavaScript port of the TTLock Android SDK
GNU General Public License v3.0
39 stars 17 forks source link

api.on("foundDevice") never fires due to invalid uuid #1

Closed Fusseldieb closed 3 years ago

Fusseldieb commented 3 years ago

I've tried the tool yesterday on a Ubuntu Live Stick to see if it would work with my computer's Bluetooth internal Intel adapter, but it didn't seem to recognize devices properly. Either due to lack of driver(s) or just because it was bugged/incompatible. I quickly scrapped that idea and waited until the next day;

Today I got hold of my Pi 3 that I've leaned to a friend earlier this year. Now that I have my original "ttlock dev device" back, I installed it fresh, met all requirements (packages and stuff) and ran it:

pi@raspberrypi:~/ttlock-sdk-js $ hcitool dev
Devices:
        hci0    B8:27:EB:79:EF:5D
pi@raspberrypi:~/ttlock-sdk-js $ sudo hcitool lescan
LE Scan ...
EE:C6:A2:04:9A:F6 (unknown)
EE:C6:A2:04:9A:F6 S202F_f69a04
1A:37:E4:82:17:41 (unknown)
D0:D0:03:2E:40:28 (unknown)
C8:D0:83:E7:89:72 (unknown)
F3:D9:79:F8:82:9B (unknown)
F3:D9:79:F8:82:9B S202F_9b82f8
01:0B:9D:26:95:7C (unknown)
50:32:37:90:2A:5F (unknown)
50:32:37:90:2A:5F (unknown)
36:22:DB:78:F2:E7 (unknown)
98:06:3C:1D:7C:B5 (unknown)
^Cpi@raspberrypi:~/ttlock-sdk-js $ sudo npm start

> ttlock-sdk-js@0.0.1 start /home/pi/ttlock-sdk-js
> npm run build && NOBLE_REPORT_ALL_HCI_EVENTS=1 node ./demo.js

> ttlock-sdk-js@0.0.1 build /home/pi/ttlock-sdk-js
> rm -rf ./dist && tsc

It just stays like this. No more output - even after 15 minutes.

As it seems, it never reaches your "connected" API status.

As you can see, there are clearly 2 locks in the lescan, but none got reported.

When does your API trigger? Newly reset locks or all of them?


Also, I just glanced through the entire repo and I must say: Impressive, how did you get hold of all of that? That's amazing!!

EDIT: Also if you're interested, I can port-forward the Pi and leave it running 24/7 in case you want to test your scripts on the real locks! None of them are "critical", so it's no problem if you crack them open at 3AM. Just let me know! - Keep in mind that doesn't mean that I won't do anything. I will, but it's certainly easier if you have real access to them ;)

Fusseldieb commented 3 years ago

I got further... Found out why it wasn't working!

https://github.com/kind3r/ttlock-sdk-js/blob/fe8c7b065e1e138d0997acd3c883f213dd7a4550/demo.js#L6-L9

If I uncomment uuids and give it an empty array, it spits out a lot of device info, but more notably:

...
S202F_f69a04  0
{"localName":"S202F_f69a04","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,72,176,0,244,249,83,101,246,154,4,162,198,238]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
...

The TTLock primary uuids are following:

pi@raspberrypi:~/ttlock-sdk-js $ sudo gatttool -i hci0 -b EE:C6:A2:04:9A:F6 -I
[EE:C6:A2:04:9A:F6][LE]> connect
Attempting to connect to EE:C6:A2:04:9A:F6
Connection successful
[EE:C6:A2:04:9A:F6][LE]> primary
attr handle: 0x0001, end grp handle: 0x0007 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0008, end grp handle: 0x000b uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0011 uuid: 00001910-0000-1000-8000-00805f9b34fb
attr handle: 0x0012, end grp handle: 0x0015 uuid: 0000180f-0000-1000-8000-00805f9b34fb
attr handle: 0x0016, end grp handle: 0x001e uuid: 0000180a-0000-1000-8000-00805f9b34fb
attr handle: 0x001f, end grp handle: 0xffff uuid: 00001530-1212-efde-1523-785feabcd123
[EE:C6:A2:04:9A:F6][LE]>
(gatttool:4601): GLib-WARNING **: 00:33:14.222: Invalid file descriptor. // Disconnects automatically after 3 sec

[EE:C6:A2:04:9A:F6][LE]> exit

Even if I put one of these primary uuids inside the array, it doesn't show anything again.

EDIT: Got further. If I take the "serviceUuids" string "1910" in the log above and paste it inside the uuids, like below:

const uuids = ["1910"];
const api = new TTLockClient({
  uuids: uuids
});

It shows:


undefined  0
{"txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,74,176,0,244,249,83,101,155,130,248,121,217,243]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_9b82f8  0
{"localName":"S202F_9b82f8","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,74,176,0,244,249,83,101,155,130,248,121,217,243]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
undefined  0
{"txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,72,176,0,244,249,83,101,246,154,4,162,198,238]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_f69a04  0
{"localName":"S202F_f69a04","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,72,176,0,244,249,83,101,246,154,4,162,198,238]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_9b82f8  0
{"localName":"S202F_9b82f8","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,74,176,0,244,249,83,101,155,130,248,121,217,243]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_9b82f8  0
{"localName":"S202F_9b82f8","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,74,176,0,244,249,83,101,155,130,248,121,217,243]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_f69a04  0
{"localName":"S202F_f69a04","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,72,176,0,244,249,83,101,246,154,4,162,198,238]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_f69a04  0
{"localName":"S202F_f69a04","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,72,176,0,244,249,83,101,246,154,4,162,198,238]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_9b82f8  0
{"localName":"S202F_9b82f8","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,74,176,0,244,249,83,101,155,130,248,121,217,243]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_9b82f8  0
{"localName":"S202F_9b82f8","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,74,176,0,244,249,83,101,155,130,248,121,217,243]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_f69a04  0
{"localName":"S202F_f69a04","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,72,176,0,244,249,83,101,246,154,4,162,198,238]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
S202F_f69a04  0
{"localName":"S202F_f69a04","txPowerLevel":-65,"manufacturerData":{"type":"Buffer","data":[5,3,2,16,72,176,0,244,249,83,101,246,154,4,162,198,238]},"serviceData":[],"serviceUuids":["1910"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
^C

As it seems, there are only the locks. The TVs and other devices that are nearby aren't showing up anymore.

That looks like attr handle: 0x000c, end grp handle: 0x0011 uuid: 0000 --> 1910 <-- -0000-1000-8000-00805f9b34fb from gatttool to me.

I'm doing further testing...

Fusseldieb commented 3 years ago

Made some modifications to the main demo.js to now ask on which lock to connect to and then running device.device.discoverServicesAsync() on it, but it didn't quite work:

[i] Using scanner type: noble
Scanning...
[i] Lock discovered: eec6a2049af6
[i] Lock discovered: f3d979f8829b
Finished scanning.
✔ Choose a lock to connect to › S202F_f69a04
{
  title: 'S202F_f69a04',
  value: 'eec6a2049af6',
  data: ExtendedBluetoothDevice {
    scanRecord: <Buffer 05 03 02 10 48 b0 00 f4 f9 53 65 f6 9a 04 a2 c6 ee>,
    id: 'eec6a2049af6',
    uuid: 'eec6a2049af6',
    name: 'S202F_f69a04',
    mAddress: '',
    rssi: -78,
    protocolType: 0,
    protocolVersion: 0,
    scene: 0,
    groupId: 0,
    orgId: 0,
    lockType: 0,
    isTouch: false,
    isSettingMode: false,
    isUnlock: false,
    txPowerLevel: 0,
    batteryCapacity: -1,
    date: 0,
    isWristband: false,
    isRoomLock: false,
    isSafeLock: false,
    isBicycleLock: false,
    isLockcar: false,
    isGlassLock: false,
    isPadLock: false,
    isCyLinder: false,
    isRemoteControlDevice: false,
    isDfuMode: false,
    isNoLockService: false,
    remoteUnlockSwitch: 0,
    disconnectStatus: 0,
    parkStatus: 0,
    device: Peripheral {
      _noble: [Noble],
      id: 'eec6a2049af6',
      uuid: 'eec6a2049af6',
      address: 'ee:c6:a2:04:9a:f6',
      addressType: 'public',
      connectable: true,
      advertisement: [Object],
      rssi: -74,
      services: null,
      mtu: null,
      state: 'disconnected'
    }
  }
}
noble warning: unknown peripheral eec6a2049af6

Related: https://github.com/noble/noble/issues/519#issuecomment-281782483

Looks like a disconnect happened at some point in reading the data. Unfortunately, noble brings up the error message "unknown peripheral" in this case: When the connection breaks the connection handle of the device ceases to exist resulting in this message. The real question is: Why does the connection break?

Is it even connected yet? I don't think so.

EDIT: Even using connect didn't do the trick...

await lock.data.device.connect();
await lock.data.device.discoverServicesAsync();

Results in:

...
      state: 'disconnected'
    }
  }
}
noble warning: unknown peripheral eec6a2049af6

Going to sleep now. It's literally 3AM ;)

kind3r commented 3 years ago

Great findings, this will help a lot until my lock is delivered (next week I hope). Few things I noticed since I received by BLE sniffer yesterday:

What wories me a bit is that still noble does not return any data (on my Mac) about my temp sensors which are broadcasting their info all the time as I see them with my BLE sniffer (and man there is a lot of scanning an broadcasting from a ton of devices). I think the hci thing noble is using is getting deprecated or something, at least in Ubuntu. Also I'm not sure how this would work in a containter for the purpose of HA integration. Maybe the node aproach is not the right one ? We'll see in time.

kind3r commented 3 years ago

Ok, I think I have figured it out, or at least partially.

Anyway, long story short, try it again and see if it shows the correct information about your locks.

I have also started to map the flow of the SDK in docs/ so I can figure out what happens when and the data required in each step.

Fusseldieb commented 3 years ago

Great findings, this will help a lot until my lock is delivered (next week I hope). Few things I noticed since I received by BLE sniffer yesterday:

Neither on my Live CD, as I said. On my RPi 3 it seems to somewhat work. If all doesn't work, theres still Python :)

  • I made the incorrect assumption that the data needed for the ExtendedBluetoothDevice is the manufacturerData from noble's Peripheral.advertisment object, but it's not. It should be broadcasted by one of the service uuids

Hmmm....

  • I now have more knowledge about BLE in general and I should be able to get the scan info right during this weekend (hopefuly)

That's cool!

Also I'm not sure how this would work in a containter for the purpose of HA integration.

Well, the ttlock-sdk-js that we're building is in my opinion basically just a "proof-of-concept". If it works, it could be ported to an ESP32 which is cheap, tiny, supports BLE and uses almost no power. It would be perfect to act as a Gateway. We could still use the ttlock-sdk-js as it is for who wants to let it run on a separate RPi 3 and pair it with their HA over MQTT, API or whatever. These are just some ideas. Would love to hear more about your opinions though.

Maybe the node aproach is not the right one ? We'll see in time.

Like I said, there is still Python. I'm also not a wizard in this language, but to build a working thing, it should be enough.

not sure why noble messes up the uuid filters, but I added 1910 to the list so it should now find the locks

Me neither, but is it what it is ahaha

The issue is that the mac address of the device in the data is not where it should be.

Yep, I saw that. mAddress is empty for some reason...

Maybe the SDK has a bug there as it reads the mac address before from the android api and never reaches that part of the code, but on MacOS you don't get the mac when scanning, just a uuid (security stuff I guess, so that some rogue software does not get a list of the bluetooth devices around you so software has to proxy pairing via the OS using that uuid; after pairing the mac address is available).

That's a theory... But I think in practice it's just broken heh After all, nowaday routers still spit their MACs around all the time. Some ISPs in my city use the MAC of their routers as the WiFi password, sometimes just inverted or cut to size.

Anyway, long story short, try it again and see if it shows the correct information about your locks.

Will definitively do that. I'll report back soon.

I have also started to map the flow of the SDK in docs/ so I can figure out what happens when and the data required in each step.

Aah, nice work!

EDIT: Now it's broken:

pi@raspberrypi:~/ttlock-sdk-js $ sudo npm start

> ttlock-sdk-js@0.0.1 start /home/pi/ttlock-sdk-js
> npm run build && NOBLE_REPORT_ALL_HCI_EVENTS=1 node ./demo.js

> ttlock-sdk-js@0.0.1 build /home/pi/ttlock-sdk-js
> rm -rf ./dist && tsc

processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
processLeAdvertisingReport: Caught illegal packet (buffer overflow): RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds

EDIT2: Yep, found out the culprit above. If I just return without parsing anything - like that:

  parseManufacturerData(manufacturerData: Buffer) {
    return
    // TODO: check offset is within the limits of the Buffer

It returns:

ExtendedBluetoothDevice {
  id: 'eec6a2049af6',
  uuid: 'eec6a2049af6',
  name: 'S202F_f69a04',
  mAddress: '',
  rssi: -78,
  protocolType: 0,
  protocolVersion: 0,
  scene: 0,
  groupId: 0,
  orgId: 0,
  lockType: 0,
  isTouch: false,
  isSettingMode: false,
  isUnlock: false,
  txPowerLevel: 0,
  batteryCapacity: -1,
  date: 0,
  isWristband: false,
  isRoomLock: false,
  isSafeLock: false,
  isBicycleLock: false,
  isLockcar: false,
  isGlassLock: false,
  isPadLock: false,
  isCyLinder: false,
  isRemoteControlDevice: false,
  isDfuMode: false,
  isNoLockService: false,
  remoteUnlockSwitch: 0,
  disconnectStatus: 0,
  parkStatus: 0,
  device: Peripheral {
    _noble: Noble {
      initialized: true,
      address: 'b8:27:eb:79:ef:5d',
      _state: 'poweredOn',
      _bindings: [NobleBindings],
      _peripherals: [Object],
      _services: [Object],
      _characteristics: [Object],
      _descriptors: [Object],
      _discoveredPeripheralUUids: [Array],
      _events: [Object: null prototype],
      _eventsCount: 6,
      _allowDuplicates: true
    },
    id: 'eec6a2049af6',
    uuid: 'eec6a2049af6',
    address: 'ee:c6:a2:04:9a:f6',
    addressType: 'public',
    connectable: true,
    advertisement: {
      localName: 'S202F_f69a04',
      txPowerLevel: -65,
      manufacturerData: <Buffer 05 03 02 10 4a b0 00 f4 f9 53 65 f6 9a 04 a2 c6 ee>,
      serviceData: [],
      serviceUuids: [Array],
      solicitationServiceUuids: [],
      serviceSolicitationUuids: []
    },
    rssi: -78,
    services: null,
    mtu: null,
    state: 'disconnected'
  }
}

Letting it parse without the return and putting parseManufacturerData in a try catch gives me following:

RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
    at boundsError (internal/buffer.js:81:11)
    at Buffer.readInt8 (internal/buffer.js:421:5)
    at ExtendedBluetoothDevice.parseManufacturerData (/home/pi/ttlock-sdk-js/dist/scanner/ExtendedBluetoothDevice.js:93:50)
    at new ExtendedBluetoothDevice (/home/pi/ttlock-sdk-js/dist/scanner/ExtendedBluetoothDevice.js:53:14)
    at NobleScanner.createFromPeripheral (/home/pi/ttlock-sdk-js/dist/scanner/NobleScanner.js:94:24)
    at NobleScanner.onNobleDiscover (/home/pi/ttlock-sdk-js/dist/scanner/NobleScanner.js:73:27)
    at Noble.emit (events.js:315:20)
    at Noble.onDiscover (/home/pi/ttlock-sdk-js/node_modules/@abandonware/noble/lib/noble.js:196:10)
    at NobleBindings.emit (events.js:315:20)
    at NobleBindings.onDiscover (/home/pi/ttlock-sdk-js/node_modules/@abandonware/noble/lib/hci-socket/bindings.js:169:10) {
  code: 'ERR_BUFFER_OUT_OF_BOUNDS'
}

The error is happening exactly here: this.protocolType = manufacturerData.readInt8(offset++);

Strangely enough, it should work. Take a look at this screenshot: https://user-images.githubusercontent.com/4715129/99894446-563a2380-2c62-11eb-95b4-d6088f17cf41.png

EDIT: Doing a console.log(manufacturerData, manufacturerData.length) it gives me that the buffer is empty: <Buffer > 0, despite ExtendedBluetoothDevice having it, as you see above... It tries to read an empty buffer.

I think you did an oopsie here:

var manufacturerData = Buffer.from([]);
    if (peripheral.advertisement.manufacturerData) {
      const manufacturerData = peripheral.advertisement.manufacturerData;
    }

You define a var, but then a const with the same name. The var somehow overwrites the const below and delivers an empty buffer. Let me try it.

EDIT: BINGO! WORKING!

I'll do a PR fixing it.

Also, you still haven't answered if I can provide you with the SSH from my Pi so you can debug better. I would gladly do that!

kind3r commented 3 years ago

Ah, I was too tired I guess and I missed that as I only tested the parsing itself and after quickly patched the constructor. Glad that's working now and great find!

I just checked the tracking and my lock should be here monday or tuesday at most. In the meantime I will go further with the lock init process investigation. So I don't think I will need the ssh access just yet. I might setup a PI myself to test on since the Ubuntu scanning is broken.

And speaking of the init process, I assume there is a way to reset the lock to defaults in case something is messed up :) I'm asking cause I remember while looking at the flutter example that only has a few test commands (init lock, set passcode to some preset, reset lock) there was a warning tha you should reset the lock after playing with it otherwise you will not get access to the lock anymore (probably some bad chinese translation). I assume this is because that app does not store the keys, passwords etc from the lock init process so after you exit the app you won't be able to connect to the lock. But you should still be able to hardware factory reset it I guess.

You have some valid points about the ESP gateway (TTLock also provides one), but since it can work straight on the PI I don't think there is a real need for one more device. Unless of course your lock is too far away from the PI, and this could be the case. As I tried to have the BLE dependency isolated from the rest of the lib it should theoreticaly be fairly easy to swap with something else, depending on the platform you run it on. I'm also interested to see if it can run in docker, not sure how the mapping of the bluetooth device works there.

I think we can close this one and open a new issue about the init process. What do you think ?

Fusseldieb commented 3 years ago

Ah, I was too tired I guess and I missed that as I only tested the parsing itself and after quickly patched the constructor. Glad that's working now and great find!

Thanks!

I just checked the tracking and my lock should be here monday or tuesday at most. In the meantime I will go further with the lock init process investigation. So I don't think I will need the ssh access just yet. I might setup a PI myself to test on since the Ubuntu scanning is broken.

Nice! A Pi certainly helps in our case. Tip: After you setup your Pi, open remote SSH in VSCode (Guide) since this makes developing much easier. It connects to the Pi over SSH and you'll get access to all the files there as they were on your pc. Maybe you already know this, but in case you didn't... now you do.

And speaking of the init process, I assume there is a way to reset the lock to defaults in case something is messed up :) I'm asking cause I remember while looking at the flutter example that only has a few test commands (init lock, set passcode to some preset, reset lock) there was a warning tha you should reset the lock after playing with it otherwise you will not get access to the lock anymore (probably some bad chinese translation). I assume this is because that app does not store the keys, passwords etc from the lock init process so after you exit the app you won't be able to connect to the lock. But you should still be able to hardware factory reset it I guess.

Yep, I'm almost certain there's a reset button. Never needed to reset the lock, so I don't know where it is, but I will find it ;)

You have some valid points about the ESP gateway (TTLock also provides one), but since it can work straight on the PI I don't think there is a real need for one more device. Unless of course your lock is too far away from the PI, and this could be the case. As I tried to have the BLE dependency isolated from the rest of the lib it should theoreticaly be fairly easy to swap with something else, depending on the platform you run it on. I'm also interested to see if it can run in docker, not sure how the mapping of the bluetooth device works there.

In my house, for example, the locks are too far apart and there would be multiple ESPs needed (or Pis). About docker, well, tbh I don't know much about it. I just use it, know how to start, stop or list containers and that's basically it. That means I don't know how that'll work, but I'm sure there's a way; ESPHome, for instance, which is inside a Docker container, communicates with the USB devices attached to the PC, so I guess it would be somewhat similar.

I think we can close this one and open a new issue about the init process. What do you think ?

That's a good idea!