What happened?
Receiving data from my PWS (personal weather station) gives an error
2026-03-14 11:18:00.250597 ERROR AppDaemon: ------------------------------------------------------------
2026-03-14 11:18:00.250953 ERROR AppDaemon: Unexpected error during API call
2026-03-14 11:18:00.251287 ERROR AppDaemon: ------------------------------------------------------------
2026-03-14 11:18:00.252903 ERROR AppDaemon: Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/appdaemon/http.py", line 887, in call_app_endpoint
ret, code = await self.dispatch_app_endpoint(endpoint, request)
^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 0)
2026-03-14 11:18:00.253242 ERROR AppDaemon: ------------------------------------------------------------
Analysis of the messages the PWS pushes:
headers: "rawHeaders":["HOST","","Connection","Close","Content-Type","application/x-www-form-urlencoded","Content-Length","474"]
data: {"PASSKEY":"xxxxxxxxxxx","stationtype":"EasyWeatherV1.7.5", "dateutc":"2026-03-11 21:45:28","tempinf":"72.5", ...}
My appdeamon code (just basic):
from typing import Literal
import appdaemon.plugins.hass.hassapi as hass
class Pws(hass.Hass):
def initialize(self):
http_endpoint = self.args.get('http_endpoint', 'pwsdata')
self.register_endpoint(self._httpHandler, http_endpoint, methods=['POST'])
self.log(f'Endpoint {http_endpoint} registered', level='INFO')
def _httpHandler(self, data, **kwargs) -> tuple[dict[str, str], Literal[200]]:
self.log(f'-------------- Received data --------------')
self.log(f'data: {data}')
self.log(f'kwargs: {kwargs}')
self.log(f'-------------------------------------------')
#for k,v in data.items():
# self.log(f'arg: {k}={v}')
return {'status': 'ok'}, 200
I run appdaemon as docker container. As info: I made the appdeamon code available for my VScode editor:
From the vs terminal run:
docker cp 420_appdaemon:/usr/local/lib/python3.12/site-packages/appdaemon ~/Desktop/docker/athome/420_appdaemon/
then add in this to the docker-compose in volumes:
- ${BASEDIR}appdaemon/appdaemon:/usr/local/lib/python3.12/site-packages/appdaemon
Modified 2 methods in http.py
The first one (call_app_endpoint) is merely cosmetic:
@securedata
# @next-release get requests object in somehow
async def call_app_endpoint(self, request):
endpoint = request.match_info.get("endpoint")
try:
ret, code = await self.dispatch_app_endpoint(endpoint, request)
except Exception:
self.logger.error("-" * 60)
self.logger.error("Unexpected error during API call")
self.logger.error(traceback.format_exc())
self.logger.error("-" * 60)
return self.get_response(request, 500, "Internal Server Error")
# Normalize return values; Ensure the body is always JSON-serializable
if not isinstance(ret, dict):
ret = {"result": ret}
# Standardized error handling
if code == 404:
return self.get_response(request, 404, "Endpoint not found")
if code == 500:
return self.get_response(request, 500, "Internal Server Error")
# Log success or non-fatal errors
status_text = "OK" if 200 <= code < 300 else "ERROR"
self.access.info("API Call to %s: status: %s %s", endpoint, code, status_text)
# Always return JSON
return web.json_response(ret, status=code, dumps=utils.convert_json)
But the second (dispatch_app_endpoint) was needed to get my code working.
It uses the header content type to call the appropriate request method.
I'm not an expert, so probably this isn't completely monkey-proof, but it worked for me.
An an extra feature I added an extra argument that can be set in the register_endpoint call.
If return_as_text=True, then the dispatch_app_endpoint function returns a string (and does not look at the header).
This to deal with some poor designed IOT device - interfaces.
async def dispatch_app_endpoint(self, endpoint, request):
callback = None
rargs = {"request": request}
# Find the endpoint callback
for name in self.app_endpoints:
if callback:
break
for data in self.app_endpoints.get(name, {}).values():
if data["endpoint"] == endpoint:
callback = data["callback"]
rargs.update(data.get("kwargs", {}))
break
# No endpoint found
if callback is None:
return self.get_response(request, 404, "Endpoint not found")
return_as_text = rargs.get("return_as_text", False)
if return_as_text:
#self.logger.info("Endpoint '%s' is configured to return text response", endpoint)
args = {"text": await request.text()}
elif request.method == "POST": # Parse POST arguments safely
try:
ctype = (request.content_type or "").split(";")[0]
self.logger.info(f"Received POST with content type '{ctype}'")
if ctype == "application/json":
args = await request.json()
elif ctype in ("application/x-www-form-urlencoded", "multipart/form-data"):
args = dict(await request.post())
elif ctype == "text/plain":
args = {"text": await request.text()}
else: # Unknown or missing content type
body = await request.text()
args = {"raw": body} if body else {}
self.logger.info(f"Received args: '{args}'")
except json.decoder.JSONDecodeError:
return self.get_response(request, 400, "JSON Decode Error")
else: # GET → query parameters
args = request.query
# Call the endpoint callback
use_dictionary_unpacking = utils.has_expanded_kwargs(callback)
try:
if asyncio.iscoroutinefunction(callback):
if use_dictionary_unpacking:
return await callback(args, **rargs)
else:
return await callback(args, rargs)
else:
if use_dictionary_unpacking:
return await utils.run_in_executor(self, callback, args, **rargs)
else:
return await utils.run_in_executor(self, callback, args, rargs)
except Exception:
self.logger.error("-" * 60)
self.logger.error("Unexpected error during endpoint callback")
self.logger.error(traceback.format_exc())
self.logger.error("-" * 60)
return self.get_response(request, 500, "Internal Server Error")
also tested with:
>curl -X POST "http://<appdaemon IP>:5050/api/appdaemon/pwsdata" -i -H "Content-Type: application/json" -d "{\"key1\":\"value1\",\"key2\":\"value2\"}"
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 16
Date: Sat, 14 Mar 2026 12:42:51 GMT
Server: Python/3.12 aiohttp/3.11.12
{"status": "ok"}
>curl -X POST "http://<appdaemon IP>:5050/api/appdaemon/pwsdata" -i -H "Content-Type: text/plain" -d "{\"key1\":\"value1\",\"key2\":\"value2\"}"
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 16
Date: Sat, 14 Mar 2026 12:43:24 GMT
Server: Python/3.12 aiohttp/3.11.12
{"status": "ok"}
>curl -X POST "http://<appdaemon IP>:5050/api/appdaemon/pwsdata" -i -d "{\"key1\":\"value1\",\"key2\":\"value2\"}"
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 16
Date: Sat, 14 Mar 2026 12:43:34 GMT
Server: Python/3.12 aiohttp/3.11.12
{"status": "ok"}
Version
4.5.11
Installation type
Docker container
Relevant log output
2026-03-14 11:18:00.250597 ERROR AppDaemon: ------------------------------------------------------------
2026-03-14 11:18:00.250953 ERROR AppDaemon: Unexpected error during API call
2026-03-14 11:18:00.251287 ERROR AppDaemon: ------------------------------------------------------------
2026-03-14 11:18:00.252903 ERROR AppDaemon: Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/appdaemon/http.py", line 887, in call_app_endpoint
ret, code = await self.dispatch_app_endpoint(endpoint, request)
^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 0)
2026-03-14 11:18:00.253242 ERROR AppDaemon: ------------------------------------------------------------
Relevant code in the app or config file that caused the issue
Anything else?
No response
What happened?
Receiving data from my PWS (personal weather station) gives an error
Analysis of the messages the PWS pushes:
headers: "rawHeaders":["HOST","","Connection","Close","Content-Type","application/x-www-form-urlencoded","Content-Length","474"]
data: {"PASSKEY":"xxxxxxxxxxx","stationtype":"EasyWeatherV1.7.5", "dateutc":"2026-03-11 21:45:28","tempinf":"72.5", ...}
My appdeamon code (just basic):
I run appdaemon as docker container. As info: I made the appdeamon code available for my VScode editor:
From the vs terminal run:
docker cp 420_appdaemon:/usr/local/lib/python3.12/site-packages/appdaemon ~/Desktop/docker/athome/420_appdaemon/then add in this to the docker-compose in volumes:
- ${BASEDIR}appdaemon/appdaemon:/usr/local/lib/python3.12/site-packages/appdaemonModified 2 methods in http.py
The first one (call_app_endpoint) is merely cosmetic:
But the second (dispatch_app_endpoint) was needed to get my code working.
It uses the header content type to call the appropriate request method.
I'm not an expert, so probably this isn't completely monkey-proof, but it worked for me.
An an extra feature I added an extra argument that can be set in the register_endpoint call.
If return_as_text=True, then the dispatch_app_endpoint function returns a string (and does not look at the header).
This to deal with some poor designed IOT device - interfaces.
also tested with:
Version
4.5.11
Installation type
Docker container
Relevant log output
Relevant code in the app or config file that caused the issue
Anything else?
No response