From 3c14058e6614dbbd4304f1ad8356e0504305076d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 3 May 2026 06:17:29 +0000 Subject: [PATCH 1/3] Use jsonpath-ng extended parser for JSON_PATHS Switches the JSON HTTP and MQTT powermeters from `jsonpath_ng.parse` to `jsonpath_ng.ext.parse` so users can sanitize values inside the path expression with `split` or `sub` extensions. This lets sources like openHAB's `Number:Power` items (which serialize as `"331.74 W"`) be consumed without the float conversion blowing up on the unit suffix. Refs #348 https://claude.ai/code/session_015ezHVP9TLKv42AeEEwtboq --- CHANGELOG.md | 1 + README.md | 7 +++++++ config.ini.example | 4 ++++ src/astrameter/powermeter/json_http.py | 2 +- src/astrameter/powermeter/json_http_test.py | 11 +++++++++++ src/astrameter/powermeter/mqtt.py | 2 +- 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59216c28..bf301131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - **Added** `exc_info` to logger warnings for better debugging ([#307](https://github.com/tomquist/astrameter/pull/307)). ### Changed +- **Upgraded** `JSON_PATHS` parsing in the JSON HTTP and MQTT powermeters to the `jsonpath-ng` extended syntax, so values that arrive with a unit suffix (e.g. openHAB `Number:Power` returning `"331.74 W"`) can be sanitized inside the path with `` `split(...)` `` or `` `sub(/regex/, replacement)` `` instead of failing the float conversion ([#348](https://github.com/tomquist/astrameter/issues/348)). - **Switched** the Home Assistant powermeter integration from REST polling to the WebSocket API ([#232](https://github.com/tomquist/astrameter/pull/232)). - **Expanded** Shelly emulation logs to report battery detection, inactivity, and reconnection events ([#241](https://github.com/tomquist/astrameter/pull/241)). - **Reduced** throttling output noise by replacing unconditional `print` calls in `ThrottledPowermeter` with structured logging (`logger.debug` for routine wait/fetch/cache messages; failures remain at error level) ([#251](https://github.com/tomquist/astrameter/pull/251)). diff --git a/README.md b/README.md index c6840e64..dd15bd2f 100644 --- a/README.md +++ b/README.md @@ -717,6 +717,13 @@ PASSWORD = pass (Optional) HEADERS = Authorization: Bearer token ``` +`JSON_PATHS` is parsed with the [`jsonpath-ng` extended syntax](https://github.com/h2non/jsonpath-ng#extensions), so you can chain extensions like `` `split(...)` `` or `` `sub(/regex/, replacement)` `` to massage the value before it's converted to a float. For example, an openHAB `Number:Power` item returns `"331.74 W"` — strip the unit with either of: + +```ini +JSON_PATHS = $.state.`split( , 0, -1)` +JSON_PATHS = $.state.`sub(/[^0-9.\-]+$/, )` +``` + ### TQ Energy Manager ```ini diff --git a/config.ini.example b/config.ini.example index fd613ac9..1fa8aec3 100644 --- a/config.ini.example +++ b/config.ini.example @@ -279,6 +279,10 @@ THROTTLE_INTERVAL = 0 # [JSON_HTTP] #URL = http://example.com/api #JSON_PATHS = $.power +## JSON_PATHS supports jsonpath-ng extensions; strip a unit suffix like +## "331.74 W" with `split` or `sub`: +##JSON_PATHS = $.state.`split( , 0, -1)` +##JSON_PATHS = $.state.`sub(/[^0-9.\-]+$/, )` #USERNAME = user #PASSWORD = pass #HEADERS = Authorization: Bearer token diff --git a/src/astrameter/powermeter/json_http.py b/src/astrameter/powermeter/json_http.py index 1e01e5cc..e1103e64 100644 --- a/src/astrameter/powermeter/json_http.py +++ b/src/astrameter/powermeter/json_http.py @@ -2,7 +2,7 @@ import aiohttp from aiohttp import BasicAuth, ClientTimeout -from jsonpath_ng import parse +from jsonpath_ng.ext import parse from astrameter.config.logger import logger diff --git a/src/astrameter/powermeter/json_http_test.py b/src/astrameter/powermeter/json_http_test.py index 6c5570f0..963e7966 100644 --- a/src/astrameter/powermeter/json_http_test.py +++ b/src/astrameter/powermeter/json_http_test.py @@ -23,6 +23,17 @@ async def test_three_phase(mock_aiohttp_session): await meter.stop() +async def test_strips_unit_suffix_via_jsonpath_ext(mock_aiohttp_session): + mock_aiohttp_session.set_json({"state": "331.74 W"}) + with patch("aiohttp.ClientSession", return_value=mock_aiohttp_session): + meter = JsonHttpPowermeter( + "http://localhost", "$.state.`sub(/[^0-9.\\-]+$/, )`" + ) + await meter.start() + assert await meter.get_powermeter_watts() == [331.74] + await meter.stop() + + async def test_headers_and_auth(mock_aiohttp_session): mock_aiohttp_session.set_json({"power": 50}) with patch("aiohttp.ClientSession", return_value=mock_aiohttp_session) as mock_cls: diff --git a/src/astrameter/powermeter/mqtt.py b/src/astrameter/powermeter/mqtt.py index 6a9521c2..d6098402 100644 --- a/src/astrameter/powermeter/mqtt.py +++ b/src/astrameter/powermeter/mqtt.py @@ -4,7 +4,7 @@ import ssl import aiomqtt -from jsonpath_ng import parse +from jsonpath_ng.ext import parse from astrameter.config.logger import logger From 9aa1f9d384cabd43b6b170e22b1248663fb98c57 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 3 May 2026 07:16:23 +0000 Subject: [PATCH 2/3] Document jsonpath-ng ext syntax for MQTT JSON_PATH(S) The MQTT powermeter was switched to the extended parser in the previous commit but its docs still pointed only to the base JSONPath spec; note the ext support next to the existing JSONPath description so MQTT users can also strip units / massage payloads inline. https://claude.ai/code/session_015ezHVP9TLKv42AeEEwtboq --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dd15bd2f..af0d8f86 100644 --- a/README.md +++ b/README.md @@ -681,6 +681,8 @@ TOPIC = home/powermeter The `JSON_PATH` option is used to extract the power value from a JSON payload. The path must be a [valid JSONPath expression](https://goessner.net/articles/JsonPath/). If the payload is a simple integer value, you can omit this option. +Both `JSON_PATH` and `JSON_PATHS` are parsed with the [`jsonpath-ng` extended syntax](https://github.com/h2non/jsonpath-ng#extensions), so you can chain extensions like `` `split(...)` `` or `` `sub(/regex/, replacement)` `` to massage a payload value before it's converted to a float — for instance `$.state.`split( , 0, -1)`` or `$.state.`sub(/[^0-9.\-]+$/, )`` to strip a unit suffix like `"331.74 W"`. See the [JSON HTTP](#json-http) section below for more examples. + #### Multi-phase MQTT For three-phase setups, there are two options: From c1985259eee9da2570448b244da9f70ebc163fe3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 3 May 2026 07:36:05 +0000 Subject: [PATCH 3/3] Move JSON_PATHS ext-parser changelog entry to bottom of Changed https://claude.ai/code/session_015ezHVP9TLKv42AeEEwtboq --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf301131..82479dad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,11 +34,11 @@ - **Added** `exc_info` to logger warnings for better debugging ([#307](https://github.com/tomquist/astrameter/pull/307)). ### Changed -- **Upgraded** `JSON_PATHS` parsing in the JSON HTTP and MQTT powermeters to the `jsonpath-ng` extended syntax, so values that arrive with a unit suffix (e.g. openHAB `Number:Power` returning `"331.74 W"`) can be sanitized inside the path with `` `split(...)` `` or `` `sub(/regex/, replacement)` `` instead of failing the float conversion ([#348](https://github.com/tomquist/astrameter/issues/348)). - **Switched** the Home Assistant powermeter integration from REST polling to the WebSocket API ([#232](https://github.com/tomquist/astrameter/pull/232)). - **Expanded** Shelly emulation logs to report battery detection, inactivity, and reconnection events ([#241](https://github.com/tomquist/astrameter/pull/241)). - **Reduced** throttling output noise by replacing unconditional `print` calls in `ThrottledPowermeter` with structured logging (`logger.debug` for routine wait/fetch/cache messages; failures remain at error level) ([#251](https://github.com/tomquist/astrameter/pull/251)). - **Improved** Shelly UDP server robustness by adding socket timeouts to avoid hangs during shutdown and testing ([#233](https://github.com/tomquist/astrameter/pull/233)). +- **Upgraded** `JSON_PATHS` parsing in the JSON HTTP and MQTT powermeters to the `jsonpath-ng` extended syntax, so values that arrive with a unit suffix (e.g. openHAB `Number:Power` returning `"331.74 W"`) can be sanitized inside the path with `` `split(...)` `` or `` `sub(/regex/, replacement)` `` instead of failing the float conversion ([#349](https://github.com/tomquist/astrameter/pull/349)). ### Fixed - **Fixed** Modbus `UNIT_ID` handling and clarified Home Assistant entity ID configuration in the docs ([#191](https://github.com/tomquist/astrameter/pull/191), [#195](https://github.com/tomquist/astrameter/pull/195)).