Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## heishamon 0.8.0

Read `hp_power_w` from `main/Heat_Power_Consumption` (TOP16), with a
`power_topic` override. This is the heat-mode input, not total electrical
input in cooling or DHW mode. The new reading remains unverified on
hardware. Invalid negative or non-finite power values clear the reading;
newer pumps can send −200 on this legacy topic. Power expires after
60 seconds without its own update, even if other topics keep arriving.

Use `hp_outdoor_temp_c` for outdoor temperature, matching the other
heat-pump drivers and FTW's Heating view. Prior history keeps its old key.

## heishamon 0.7.0

Declare the existing `set_heat_curve_offset` control: −3…+3 °C, in steps
Expand Down Expand Up @@ -315,7 +327,6 @@ Driver versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html
comments. No behaviour change.

### Added

- **acuvim** 0.4.1 — Accuenergy Acuvim II three-phase revenue-grade meter,
migrated from the Blixt L1 driver source (POI meter on Blixt
SvK sites). One bundled FC03 float32 block + a bounded probe of the
Expand Down
4 changes: 2 additions & 2 deletions SUPPORT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ Catalog source is not proof that a target can install or run a driver.
| growatt | 2.1.2 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| hardybarth | 1.0.1 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| hardybarth | 1.0.1 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| heishamon | 0.7.0 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| heishamon | 0.7.0 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| heishamon | 0.8.0 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| heishamon | 0.8.0 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| hello | 1.1.2 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| hello | 1.1.2 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| huawei | 2.1.3 | ftw-core | not_assessed | — | not_recorded | not_assessed |
Expand Down
2 changes: 1 addition & 1 deletion devices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,7 @@ manufacturers:
protocols:
- protocol: mqtt
driver: "heishamon"
version: "0.7.0"
version: "0.8.0"
ders: [heatpump]
control: true
firmware_versions: ""
Expand Down
48 changes: 42 additions & 6 deletions drivers/lua/heishamon.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,22 @@
-- min_offset: -3
-- max_offset: 3
-- safe_offset: 0
-- power_topic: Heat_Power_Consumption
--
-- power_topic names a main/ topic carrying electrical power in W.
-- The default is Heat_Power_Consumption (TOP16), the heat-mode input.
-- It is not total input in cooling or DHW mode. Pumps with an extra data
-- block can send invalid negative values on this legacy topic; omit them.
-- This new reading has not been checked on hardware.
-- Source: https://github.com/heishamon/HeishaMon/blob/main/MQTT-Topics.md

DRIVER = {
host_api_min = 1,
host_api_max = 1,
id = "heishamon",
name = "Panasonic Aquarea (Heishamon)",
manufacturer = "Panasonic",
version = "0.7.0",
version = "0.8.0",
protocols = { "mqtt" },
capabilities = { "heatpump" },
description = "Panasonic Aquarea H/J/K/L/M-series heat pump via Heishamon MQTT bridge. Controls Zone 1 heat curve offset (Z1_Heat_Request_Temp) in range -3..+3 °C.",
Expand All @@ -41,7 +49,7 @@ DRIVER = {
verification_status = "experimental",
verified_by = { "Rolf (Runneval)" },
verified_at = "2026-06-21",
verification_notes = "Tested on WH-SXC09H3E8 (H-series) with Heishamon Large v4.1.6 on ESP32. MQTT via core-mosquitto on HA Green. Live metrics confirmed. Offset control verified via Z1_Heat_Request_Temp.",
verification_notes = "Existing metrics and offset control tested on WH-SXC09H3E8 (H-series) with Heishamon Large v4.1.6 on ESP32 on 2026-06-21. The new hp_power_w reading is not hardware-verified; its default topic reports heat-mode input, not total input in cooling or DHW mode.",
-- What an operator may command, in terms a host UI can render without
-- knowing this driver. The bounds are the defaults below; min_offset and
-- max_offset can narrow them in config, and driver_command clamps to
Expand Down Expand Up @@ -71,14 +79,17 @@ local outlet_temp = nil
local inlet_temp = nil
local target_temp = nil
local z1_offset = nil
local power_w = nil
local last_msg_ts = 0
local last_power_ts = 0
local STALE_AFTER_MS = 60000

-- Config (overridable via config.yaml)
local base_topic = "panasonic_heat_pump"
local min_offset = -3
local max_offset = 3
local safe_offset = 0
local power_topic = "Heat_Power_Consumption"

----------------------------------------------------------------------------
-- Lifecycle
Expand All @@ -92,6 +103,7 @@ function driver_init(config)
if config.min_offset then min_offset = tonumber(config.min_offset) or -3 end
if config.max_offset then max_offset = tonumber(config.max_offset) or 3 end
if config.safe_offset then safe_offset = tonumber(config.safe_offset) or 0 end
if config.power_topic then power_topic = config.power_topic end
end

-- Subscribe broadly to all Heishamon topics
Expand All @@ -104,7 +116,8 @@ function driver_init(config)

host.log("info", "Heishamon: initialized, base_topic=" .. base_topic
.. " offset_range=[" .. min_offset .. ".." .. max_offset .. "]"
.. " safe_offset=" .. safe_offset)
.. " safe_offset=" .. safe_offset
.. " power_topic=" .. power_topic)
end

function driver_poll()
Expand All @@ -114,7 +127,18 @@ function driver_poll()

for _, msg in ipairs(messages) do
local val = tonumber(msg.payload)
if val ~= nil then
if msg.topic == base_topic .. "/main/" .. power_topic then
-- Heat consumption cannot be negative. Some newer pumps use
-- -200 as an invalid value on the legacy power topic.
if val ~= nil and val == val and val >= 0 and val < math.huge then
power_w = val
last_power_ts = now
last_msg_ts = now
else
power_w = nil
last_power_ts = 0
end
elseif val ~= nil then
if msg.topic == base_topic .. "/main/Outside_Temp" then
outside_temp = val
last_msg_ts = now
Expand Down Expand Up @@ -143,14 +167,24 @@ function driver_poll()
inlet_temp = nil
target_temp = nil
z1_offset = nil
power_w = nil
last_power_ts = 0
elseif last_power_ts > 0 and (now - last_power_ts) > STALE_AFTER_MS then
host.log("warn", "Heishamon: no power reading for "
.. tostring(STALE_AFTER_MS) .. " ms — power stale")
power_w = nil
last_power_ts = 0
end

-- Emit metrics
if outside_temp ~= nil then host.emit_metric("hp_outside_temp_c", outside_temp, "°C") end
-- Emit metrics. Names are the ones FTW's heating view reads: it finds a
-- heat pump by hp_power_w and charts the outdoor temperature under
-- hp_outdoor_temp_c, the same names nibe_local and myuplink report.
if outside_temp ~= nil then host.emit_metric("hp_outdoor_temp_c", outside_temp, "°C") end
if outlet_temp ~= nil then host.emit_metric("hp_outlet_temp_c", outlet_temp, "°C") end
if inlet_temp ~= nil then host.emit_metric("hp_inlet_temp_c", inlet_temp, "°C") end
if target_temp ~= nil then host.emit_metric("hp_target_temp_c", target_temp, "°C") end
if z1_offset ~= nil then host.emit_metric("hp_z1_heat_offset", z1_offset, "°C") end
if power_w ~= nil then host.emit_metric("hp_power_w", power_w, "W") end

return 5000
end
Expand Down Expand Up @@ -205,4 +239,6 @@ function driver_cleanup()
inlet_temp = nil
target_temp = nil
z1_offset = nil
power_w = nil
last_power_ts = 0
end
60 changes: 60 additions & 0 deletions drivers/tests/lua_harness/test_heishamon_power_freshness.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
dofile("drivers/tests/lua_harness/host_mock.lua")
host.reset()
dofile("drivers/lua/heishamon.lua")
driver_init({})

host._mqtt_buffer = {
{
topic = "panasonic_heat_pump/main/Heat_Power_Consumption",
payload = "4200",
},
{
topic = "panasonic_heat_pump/main/Outside_Temp",
payload = "5",
},
}
driver_poll()

local fresh_power = host._metrics.hp_power_w
if fresh_power == nil or fresh_power.value ~= 4200 then
error("fresh Heishamon power reading was not emitted")
end

host._metrics = {}
host._millis_counter = 60100
host._mqtt_buffer = {{
topic = "panasonic_heat_pump/main/Outside_Temp",
payload = "6",
}}
driver_poll()

if host._metrics.hp_power_w ~= nil then
error("stale Heishamon power survived an unrelated fresh topic")
end

local fresh_outdoor = host._metrics.hp_outdoor_temp_c
if fresh_outdoor == nil or fresh_outdoor.value ~= 6 then
error("fresh unrelated Heishamon telemetry was dropped with stale power")
end

-- Newer heat pumps can send -200 on the old power topic. Invalid values
-- must clear the previous power reading while other measurements survive.
for _, invalid in ipairs({"-200", "nan", "1e99"}) do
host._metrics = {}
host._mqtt_buffer = {{topic="panasonic_heat_pump/main/Heat_Power_Consumption", payload="4200"}}
driver_poll()
assert(host._metrics.hp_power_w.value == 4200, "valid power did not recover")
host._metrics = {}
host._mqtt_buffer = {
{topic="panasonic_heat_pump/main/Heat_Power_Consumption", payload=invalid},
{topic="panasonic_heat_pump/main/Outside_Temp", payload="7"},
}
driver_poll()
assert(host._metrics.hp_power_w == nil, "invalid power was emitted: " .. invalid)
assert(host._metrics.hp_outdoor_temp_c.value == 7, "invalid power dropped outdoor temperature")
end

host._metrics = {}
host._mqtt_buffer = {{topic="panasonic_heat_pump/main/Heat_Power_Consumption", payload="0"}}
driver_poll()
assert(host._metrics.hp_power_w.value == 0, "measured zero power was dropped")
7 changes: 7 additions & 0 deletions drivers/tests/test_stale_reads.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ def test_mqtt_driver_does_not_emit_cached_data_on_idle_poll(driver_name):
run_lua("drivers/tests/lua_harness/test_mqtt_stale.lua", driver_name)


def test_heishamon_power_freshness_is_not_extended_by_other_topics():
run_lua(
"drivers/tests/lua_harness/test_heishamon_power_freshness.lua",
"heishamon",
)


@pytest.mark.parametrize("app", ["ProEM", "MiniPMG3", "Plus2PM"])
def test_shelly_poll_emits_nothing_when_all_reads_fail(app):
run_lua("drivers/tests/lua_harness/test_shelly_failures.lua", app)
Expand Down
6 changes: 3 additions & 3 deletions index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -331,15 +331,15 @@ drivers:
size_bytes: 2193
sha256: "517e086af899dc511c86b00460cd8ce052a8d4973edee8b018e5824d4b550d6e"
- name: "heishamon"
version: "0.7.0"
version: "0.8.0"
tier: core
protocol: mqtt
connectivity: local
setup: [bridge]
ders: [heatpump]
control: true
size_bytes: 7932
sha256: "aed6707a656a7e4e17c5edd00e8d081ed485b580d078173c13bdad75d9ffcaac"
size_bytes: 9855
sha256: "5c322f14e58a2b9aad900813bea9438127249ebae2e76a4208cd74536703d7b6"
- name: "hello"
version: "1.1.2"
tier: community
Expand Down
12 changes: 9 additions & 3 deletions manifests/heishamon.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: "heishamon"
version: "0.7.0"
version: "0.8.0"
tier: core
author: "Sourceful Labs AB"
protocol: mqtt
Expand All @@ -16,9 +16,15 @@ tested_devices:
notes: "Panasonic Aquarea H/J/K/L/M-series heat pump via Heishamon MQTT bridge. Controls Zone 1 heat curve offset (Z1_Heat_Request_Temp) in range -3..+3 °C."
min_driver_version: "0.4.0"
min_host_version: "2.0.0"
size_bytes: 7932
size_bytes: 9855
dkb_id: "heishamon"
sha256: "aed6707a656a7e4e17c5edd00e8d081ed485b580d078173c13bdad75d9ffcaac"
sha256: "5c322f14e58a2b9aad900813bea9438127249ebae2e76a4208cd74536703d7b6"
signature: ""

bytecode_sha256: ""

upstream_docs:
- url: "https://raw.githubusercontent.com/heishamon/HeishaMon/main/MQTT-Topics.md"
title: "HeishaMon MQTT topics"
kind: api_docs
url_stability: stable
2 changes: 1 addition & 1 deletion support-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@
},
{
"catalog_source": true,
"catalog_version": "0.7.0",
"catalog_version": "0.8.0",
"driver_id": "heishamon",
"targets": {
"blixt-l1": {
Expand Down
Loading