From c952a55a606fc3ef9498f064f133000253b89ef9 Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 10:26:14 +0000 Subject: [PATCH 1/8] Fixed contribution sensors --- custom_components/simple_pid_controller/sensor.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index 4f26ec7..663f617 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -69,12 +69,7 @@ async def update_pid(): output = pid(input_value) - # Calculate contributions - p_contrib = kp * (setpoint - input_value) if not p_on_m else -kp * input_value - i_contrib = pid._integral * ki - d_contrib = pid._last_output - output if pid._last_output is not None else 0.0 - - handle.last_contributions = (p_contrib, i_contrib, d_contrib) + handle.last_contributions = pid.components _LOGGER.debug( "PID input=%.2f setpoint=%.2f kp=%.2f ki=%.2f kd=%.2f => output=%.2f [P=%.2f, I=%.2f, D=%.2f]", @@ -84,9 +79,9 @@ async def update_pid(): ki, kd, output, - p_contrib, - i_contrib, - d_contrib, + pid.components[0], + pid.components[1], + pid.components[2], ) if coordinator.update_interval.total_seconds() != pid.sample_time: From 0c500eead99240b4d251a338c590a9150333927b Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 12:32:06 +0200 Subject: [PATCH 2/8] Update sensor.py --- .../simple_pid_controller/sensor.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index 663f617..aa1323e 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -72,16 +72,12 @@ async def update_pid(): handle.last_contributions = pid.components _LOGGER.debug( - "PID input=%.2f setpoint=%.2f kp=%.2f ki=%.2f kd=%.2f => output=%.2f [P=%.2f, I=%.2f, D=%.2f]", - input_value, - setpoint, - kp, - ki, - kd, - output, - pid.components[0], - pid.components[1], - pid.components[2], + f"PID input={input_value:.2f} setpoint={setpoint:.2f} " + f"kp={kp:.2f} ki={ki:.2f} kd={kd:.2f} => " + f"output={output:.2f if output is not None else 'n/a'} " + f"[P={p_contrib:.2f if p_contrib is not None else 'n/a'}, " + f"I={i_contrib:.2f if i_contrib is not None else 'n/a'}, " + f"D={d_contrib:.2f if d_contrib is not None else 'n/a'}]" ) if coordinator.update_interval.total_seconds() != pid.sample_time: From 6b8299c6b41e099ebe93b20f1b08afca10740db0 Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 12:35:53 +0200 Subject: [PATCH 3/8] Update sensor.py --- .../simple_pid_controller/sensor.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index aa1323e..3d01922 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -72,12 +72,16 @@ async def update_pid(): handle.last_contributions = pid.components _LOGGER.debug( - f"PID input={input_value:.2f} setpoint={setpoint:.2f} " - f"kp={kp:.2f} ki={ki:.2f} kd={kd:.2f} => " - f"output={output:.2f if output is not None else 'n/a'} " - f"[P={p_contrib:.2f if p_contrib is not None else 'n/a'}, " - f"I={i_contrib:.2f if i_contrib is not None else 'n/a'}, " - f"D={d_contrib:.2f if d_contrib is not None else 'n/a'}]" + "PID input=%s setpoint=%s kp=%s ki=%s kd=%s => output=%s [P=%s, I=%s, D=%s]", + input_value, + pid.setpoint, + pid.Kp, + pid.Ki, + pid.Kd, + output, + last_contributions[0], + last_contributions[1], + last_contributions[2], ) if coordinator.update_interval.total_seconds() != pid.sample_time: From 4a6644357647da9666011a6298469cc180bdfc9c Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 12:36:58 +0200 Subject: [PATCH 4/8] Update sensor.py --- custom_components/simple_pid_controller/sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index 3d01922..fea4dbd 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -79,9 +79,9 @@ async def update_pid(): pid.Ki, pid.Kd, output, - last_contributions[0], - last_contributions[1], - last_contributions[2], + handle.last_contributions[0], + handle.last_contributions[1], + handle.last_contributions[2], ) if coordinator.update_interval.total_seconds() != pid.sample_time: From a224c2aa74247b09a5791d4db24bf20ab067d042 Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 13:00:44 +0200 Subject: [PATCH 5/8] Add Diagnostics Idelta --- .../simple_pid_controller/sensor.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index fea4dbd..9b89146 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -37,7 +37,8 @@ async def async_setup_entry( pid = PID(1.0, 0.1, 0.05, setpoint=50) pid.sample_time = 10.0 pid.output_limits = (-10.0, 10.0) - + handle.last_contributions = (0,0,0,0) + async def update_pid(): """Update the PID output using current sensor and parameter values.""" input_value = handle.get_input_sensor_value() @@ -69,10 +70,15 @@ async def update_pid(): output = pid(input_value) + # save last I contribution + last_i = handle.last_contributions[1] + + #save all latest contributions handle.last_contributions = pid.components - + handle.last_contributions[3] = last_i - handle.last_contributions[1] + _LOGGER.debug( - "PID input=%s setpoint=%s kp=%s ki=%s kd=%s => output=%s [P=%s, I=%s, D=%s]", + "PID input=%s setpoint=%s kp=%s ki=%s kd=%s => output=%s [P=%s, I=%s, D=%s, dI=%s]", input_value, pid.setpoint, pid.Kp, @@ -82,6 +88,7 @@ async def update_pid(): handle.last_contributions[0], handle.last_contributions[1], handle.last_contributions[2], + handle.last_contributions[3], ) if coordinator.update_interval.total_seconds() != pid.sample_time: @@ -121,6 +128,9 @@ async def start_refresh(_: Any) -> None: hass, entry, "pid_d_contrib", "D contribution", coordinator ), PIDContributionSensor(hass, entry, "error", "Error", coordinator), + PIDContributionSensor( + hass, entry, "pid_i_delta", "I delta", coordinator + ), ] ) @@ -212,5 +222,6 @@ def native_value(self): "pid_i_contrib": contributions[1], "pid_d_contrib": contributions[2], "error": error, + "pid_i_delta": contributions[3], }.get(self._key) return round(value, 2) if value is not None else None From 7d1678cce404a74daea782b0a769295e2120ccdd Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 11:18:29 +0000 Subject: [PATCH 6/8] Added I delta sensor + tests --- .../simple_pid_controller/sensor.py | 22 ++++++++++--------- tests/test_sensor.py | 3 ++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index 9b89146..aa42e63 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -37,8 +37,8 @@ async def async_setup_entry( pid = PID(1.0, 0.1, 0.05, setpoint=50) pid.sample_time = 10.0 pid.output_limits = (-10.0, 10.0) - handle.last_contributions = (0,0,0,0) - + handle.last_contributions = (0, 0, 0, 0) + async def update_pid(): """Update the PID output using current sensor and parameter values.""" input_value = handle.get_input_sensor_value() @@ -72,11 +72,15 @@ async def update_pid(): # save last I contribution last_i = handle.last_contributions[1] - - #save all latest contributions - handle.last_contributions = pid.components - handle.last_contributions[3] = last_i - handle.last_contributions[1] - + + # save all latest contributions + handle.last_contributions = ( + pid.components[0], + pid.components[1], + pid.components[2], + last_i - pid.components[1], + ) + _LOGGER.debug( "PID input=%s setpoint=%s kp=%s ki=%s kd=%s => output=%s [P=%s, I=%s, D=%s, dI=%s]", input_value, @@ -128,9 +132,7 @@ async def start_refresh(_: Any) -> None: hass, entry, "pid_d_contrib", "D contribution", coordinator ), PIDContributionSensor(hass, entry, "error", "Error", coordinator), - PIDContributionSensor( - hass, entry, "pid_i_delta", "I delta", coordinator - ), + PIDContributionSensor(hass, entry, "pid_i_delta", "I delta", coordinator), ] ) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 509afaa..9201041 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -47,7 +47,7 @@ async def test_pid_contribution_native_value_rounding_and_none(hass, config_entr """Test that PIDContributionSensor.native_value rounds correctly and returns None for unknown key.""" handle = config_entry.runtime_data.handle # Provide known contributions - handle.last_contributions = (0.1234, 1.9876, 2.5555) + handle.last_contributions = (0.1234, 1.9876, 2.5555, 3.3789) coordinator = PIDDataCoordinator(hass, "test", lambda: 0, interval=1) # Map contribution keys to expected values @@ -56,6 +56,7 @@ async def test_pid_contribution_native_value_rounding_and_none(hass, config_entr ("pid_i_contrib", round(1.9876, 2)), ("pid_d_contrib", round(2.5555, 2)), ("error", -25), + ("pid_i_delta", round(3.3789, 2)), ("unknown_key", None), # Should return None ] From 41aec669fd131f1f111abc04d1060220ed3ebd7c Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 11:22:32 +0000 Subject: [PATCH 7/8] Hide diagnostics sensors --- custom_components/simple_pid_controller/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index aa42e63..721c586 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -203,7 +203,7 @@ def __init__( BasePIDEntity.__init__(self, hass, entry, key, name) self._attr_entity_category = EntityCategory.DIAGNOSTIC - # self._attr_entity_registry_enabled_default = False + self._attr_entity_registry_enabled_default = False self._attr_state_class = SensorStateClass.MEASUREMENT self._key = key self._handle = entry.runtime_data.handle From bd0b8dc095055a4f57e4e0ae9f81b95ae4c72f8d Mon Sep 17 00:00:00 2001 From: bvweerd Date: Wed, 18 Jun 2025 12:24:02 +0000 Subject: [PATCH 8/8] changed around dI --- custom_components/simple_pid_controller/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/simple_pid_controller/sensor.py b/custom_components/simple_pid_controller/sensor.py index 721c586..5b8e414 100644 --- a/custom_components/simple_pid_controller/sensor.py +++ b/custom_components/simple_pid_controller/sensor.py @@ -78,7 +78,7 @@ async def update_pid(): pid.components[0], pid.components[1], pid.components[2], - last_i - pid.components[1], + pid.components[1] - last_i, ) _LOGGER.debug(