vintlabs / fauxmoESP

Add voice control of your ESP32 and ESP8266 devices using Amazon Alexa
MIT License
379 stars 70 forks source link

Number of devices limited by size of "device info" response #152

Open pvint opened 3 years ago

pvint commented 3 years ago

Mostly an ESP8266 issue, but it occurs on ESP32 at higher numbers of devices.

When doing device discovery, when the Alexa device sends GET /api/2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr/lights HTTP/1.1 and there are too many devices the response gets truncated.

When (ESP)AsyncTCP doesn't have "room", the remainder gets truncated in AsyncClient::add.

Need to figure out how to split up the response (or worst case, at least warn the user about what has happened)

Ref: #150

Some debugging info:

[FAUXMO] TCP request
GET /api/2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr/lights HTTP/1.1
Host: 192.168.1.3
Accept: */*
Content-Type: application/json

[FAUXMO] isGet: true
[FAUXMO] URL: /api/2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr/lights
[FAUXMO] Handling list request
[FAUXMO] Sending device info for "Alpha 0", uniqueID = "ef89f7a81812"
[FAUXMO] Sending device info for "Alpha 1", uniqueID = "b977e8afc424"
[FAUXMO] Sending device info for "Alpha 2", uniqueID = "25494a75286e"
[FAUXMO] Sending device info for "Alpha 3", uniqueID = "1ca6a1e04c96"
[FAUXMO] Sending device info for "Alpha 4", uniqueID = "155dc5c7f475"
[FAUXMO] Sending device info for "Alpha 5", uniqueID = "15682572483a"
[FAUXMO] Sending device info for "Alpha 6", uniqueID = "478991e8c031"
[FAUXMO] Sending device info for "Alpha 7", uniqueID = "df7a12146007"
[FAUXMO] Sending device info for "Alpha 8", uniqueID = "fcfa49adff68"
[FAUXMO] Sending device info for "Alpha 9", uniqueID = "257dea39c585"
[FAUXMO] Sending device info for "Alpha 10", uniqueID = "7ca0408d1799"
[FAUXMO] Sending device info for "Alpha 11", uniqueID = "bd6857930dde"
[FAUXMO] Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 3162
Connection: close

{"1":{"type":"Extended Color Light","name":"Alpha 0","uniqueid":"ef89f7a81812","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"2":{"type":"Extended Color Light","name":"Alpha 1","uniqueid":"b977e8afc424","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"3":{"type":"Extended Color Light","name":"Alpha 2","uniqueid":"25494a75286e","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"4":{"type":"Extended Color Light","name":"Alpha 3","uniqueid":"1ca6a1e04c96","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"5":{"type":"Extended Color Light","name":"Alpha 4","uniqueid":"155dc5c7f475","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"6":{"type":"Extended Color Light","name":"Alpha 5","uniqueid":"15682572483a","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"7":{"type":"Extended Color Light","name":"Alpha 6","uniqueid":"478991e8c031","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"8":{"type":"Extended Color Light","name":"Alpha 7","uniqueid":"df7a12146007","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"9":{"type":"Extended Color Light","name":"Alpha 8","uniqueid":"fcfa49adff68","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"10":{"type":"Extended Color Light","name":"Alpha 9","uniqueid":"257dea39c585","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"11":{"type":"Extended Color Light","name":"Alpha 10","uniqueid":"7ca0408d1799","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"},"12":{"type":"Extended Color Light","name":"Alpha 11","uniqueid":"bd6857930dde","modelid":"LCT007","state":{"on":false,"bri":0,"xy":[0,0],"reachable": true},"capabilities":{"certified":false,"streaming":{"renderer":true,"proxy":false}},"swversion":"5.105.0.21169"}}
[ESPAsyncTCP] add()   room = 2920, size = 92, will_send = 92
[ESPAsyncTCP] add()   room = 2828, size = 3162, will_send = 2828
Dani-Hg commented 3 years ago

Hello

I'm quite a beginner, but I don't mind that I can't use several devices. I stumbled upon the limitation of the TCP send buffer in the LwIP. This can be adjusted. By default it is set to 5744 bytes. Twice as much buffer is required as bytes are sent. That would fit with the 2828 bytes.

It is possible to increase the buffer to 28,000 bytes. Unfortunately, I do not know how to adjust the settings.

CONFIG_LWIP_TCP_SND_BUF_DEFAULT

tcp_sndbuf (_pcb)

kzkaram commented 3 years ago

I have found that the device info in response to the initial request for listing ALL devices doesn't have to be so detailed. It looks to be sufficient just to have the device reference number, the name and the unique ID for each device. This could make this response shorter when you have multiple devices.

b1gmans commented 3 years ago

Me too

I created this shortened version:

// Add in a short version for the ALL PROGMEM const char FAUXMO_DEVICE_JSON_TEMPLATE_SHORT[] = "{" "\"type\": \"Extended color light\"," "\"name\": \"%s\"," "\"uniqueid\": \"%s\""

"}"; and called that whenever the request for ALL is made. It works perfectly - I can now add all my 15 devices, and probably more.

pvint commented 3 years ago

@all: Nice work!

My time is a bit short lately, but I will definitely be looking into the send buffer changes as mentioned by @Dani-Hg

For now, I'd like to test and implement the changes mentioned by @kzkaram & @b1gmans

If you have the time and inclination, could you submit a PR? (Just to save me a bit of time ;) )

mcspr commented 3 years ago

Just to note... no need to tweak CONFIG_LWIP_TCP_SND_BUF_DEFAULT or any lwip settings. The general flow should just accept that client->add() may fail and it should buffer the data until the client->space() != 0 when network is done sending.

i.e. implement what generic wificlient does, and stall until the network is done with the data to send another chunk. Maybe hook into onAck, which will be called when the remote accepts the data. Or, onPoll, which is periodically called (although a bit slow) while the connection is active

pvint commented 3 years ago

Good point - I'll try that. We use client-write() currently, and I think it could be changed to use client->add() then client->send()

For my own reference, here's the relevant excerpt from AsyncTCP.h:

    size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending
    bool send();//send all data added with the method above

    //write equals add()+send()
    size_t write(const char* data);
    size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true
mcspr commented 3 years ago

Pretty much, yes. ref. pretty recent update to the async-mqtt-client https://github.com/marvinroger/async-mqtt-client/blob/2991968a97193aaa6402d146490b93ea671c7e02/src/AsyncMqttClient.cpp#L399-L465 data blobs are placed into queue, and _handleQueue() is called on every opportunity - onAck, onConnected, onPoll, etc.

which closely follows what espasyncwebserver does with the websocket implementation. or what espurna does with buffered telnet server, and terminal output overall

salsabeard commented 2 years ago

Hey all, I see this thread had activity about a year ago with report that device count could be extended by slimming down device information in the responses, but it seems like this will still create a relatively low limit on number or maximum devices. When I say low limit, I'm considering the application I'm looking for in that I'm working to expose about 140 relays (chained 16 channel relay boards) to Alexa.

Unfortunately my knowledge of APIs is pretty limited, but my mind immediately asks the question, "Can you tell the requester to expect multiple responses?" Basically, when Alexa initiates a discovery, can we tell it to expect multiple responses back and then effectively break up the devices into distributed bulk payloads of say 10 devices each?