Skip to content

Commit d442140

Browse files
feat: add heat-status CLI set command for TUI parity
Add _set_heat_status() to cli.py following the existing _set_* pattern, dispatching from cmd_set() for "heat-status" on/off. Update _SET_PARAM_NAMES and argparse help text to include heat-status. Add tests for on, off, invalid values, and dispatch integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e9cb295 commit d442140

2 files changed

Lines changed: 84 additions & 6 deletions

File tree

src/flameconnect/cli.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@
130130

131131
_SET_PARAM_NAMES = (
132132
"mode, flame-speed, brightness, pulsating, flame-color,"
133-
" media-theme, heat-mode, heat-temp, timer, temp-unit,"
134-
" flame-effect, media-light, media-color, overhead-light,"
135-
" overhead-color, ambient-sensor"
133+
" media-theme, heat-status, heat-mode, heat-temp, timer,"
134+
" temp-unit, flame-effect, media-light, media-color,"
135+
" overhead-light, overhead-color, ambient-sensor"
136136
)
137137

138138

@@ -481,6 +481,9 @@ async def cmd_set(
481481
if param == "ambient-sensor":
482482
await _set_ambient_sensor(client, fire_id, value)
483483
return
484+
if param == "heat-status":
485+
await _set_heat_status(client, fire_id, value)
486+
return
484487

485488
print(f"Error: unknown parameter '{param}'. Valid: {_SET_PARAM_NAMES}.")
486489
sys.exit(1)
@@ -810,6 +813,28 @@ async def _set_ambient_sensor(
810813
print(f"Ambient sensor set to {value}.")
811814

812815

816+
async def _set_heat_status(
817+
client: FlameConnectClient, fire_id: str, value: str
818+
) -> None:
819+
"""Set the heater on or off."""
820+
from flameconnect.models import HeatStatus
821+
822+
lookup: dict[str, HeatStatus] = {"on": HeatStatus.ON, "off": HeatStatus.OFF}
823+
if value not in lookup:
824+
valid = ", ".join(lookup)
825+
print(f"Error: heat-status must be one of: {valid}.")
826+
sys.exit(1)
827+
heat_status = lookup[value]
828+
overview = await client.get_fire_overview(fire_id)
829+
current = _find_param(overview.parameters, HeatParam)
830+
if current is None:
831+
print("Error: no HeatSettings parameter found.")
832+
sys.exit(1)
833+
new_param = replace(current, heat_status=heat_status)
834+
await client.write_parameters(fire_id, [new_param])
835+
print(f"Heat status set to {value}.")
836+
837+
813838
async def cmd_tui(*, verbose: bool = False) -> None:
814839
"""Launch the TUI, showing install message if missing."""
815840
try:
@@ -866,9 +891,10 @@ def build_parser() -> argparse.ArgumentParser:
866891
"param",
867892
help=(
868893
"Parameter name: mode, flame-speed, brightness, pulsating,"
869-
" flame-color, media-theme, heat-mode, heat-temp, timer,"
870-
" temp-unit, flame-effect, media-light, media-color,"
871-
" overhead-light, overhead-color, ambient-sensor"
894+
" flame-color, media-theme, heat-status, heat-mode,"
895+
" heat-temp, timer, temp-unit, flame-effect, media-light,"
896+
" media-color, overhead-light, overhead-color,"
897+
" ambient-sensor"
872898
),
873899
)
874900
sp_set.add_argument("value", help="Value to set")

tests/test_cli_set.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
_set_flame_effect,
1818
_set_flame_speed,
1919
_set_heat_mode,
20+
_set_heat_status,
2021
_set_heat_temp,
2122
_set_media_color,
2223
_set_media_light,
@@ -573,6 +574,16 @@ async def test_dispatch_temp_unit(self, mock_api, token_auth):
573574
key = ("POST", URL(WRITE_URL))
574575
assert len(mock_api.requests[key]) == 1
575576

577+
async def test_dispatch_heat_status(self, mock_api, token_auth, overview_payload):
578+
mock_api.get(OVERVIEW_URL, payload=overview_payload)
579+
mock_api.post(WRITE_URL, payload={})
580+
581+
async with FlameConnectClient(token_auth) as client:
582+
await cmd_set(client, FIRE_ID, "heat-status", "on")
583+
584+
key = ("POST", URL(WRITE_URL))
585+
assert len(mock_api.requests[key]) == 1
586+
576587
async def test_dispatch_unknown_param(self, mock_api, token_auth, capsys):
577588
async with FlameConnectClient(token_auth) as client:
578589
with pytest.raises(SystemExit):
@@ -729,6 +740,47 @@ async def test_set_ambient_sensor_invalid(self, mock_api, token_auth, capsys):
729740
assert "Error" in captured.out
730741

731742

743+
# ---------------------------------------------------------------------------
744+
# _set_heat_status
745+
# ---------------------------------------------------------------------------
746+
747+
748+
class TestSetHeatStatus:
749+
"""Tests for the _set_heat_status CLI command."""
750+
751+
async def test_set_heat_status_on(self, mock_api, token_auth, overview_payload):
752+
mock_api.get(OVERVIEW_URL, payload=overview_payload)
753+
mock_api.post(WRITE_URL, payload={})
754+
755+
async with FlameConnectClient(token_auth) as client:
756+
await _set_heat_status(client, FIRE_ID, "on")
757+
758+
key = ("POST", URL(WRITE_URL))
759+
calls = mock_api.requests[key]
760+
assert len(calls) == 1
761+
body = calls[0].kwargs["json"]
762+
assert body["FireId"] == FIRE_ID
763+
assert body["Parameters"][0]["ParameterId"] == 323
764+
765+
async def test_set_heat_status_off(self, mock_api, token_auth, overview_payload):
766+
mock_api.get(OVERVIEW_URL, payload=overview_payload)
767+
mock_api.post(WRITE_URL, payload={})
768+
769+
async with FlameConnectClient(token_auth) as client:
770+
await _set_heat_status(client, FIRE_ID, "off")
771+
772+
key = ("POST", URL(WRITE_URL))
773+
calls = mock_api.requests[key]
774+
assert len(calls) == 1
775+
776+
async def test_set_heat_status_invalid(self, mock_api, token_auth, capsys):
777+
async with FlameConnectClient(token_auth) as client:
778+
with pytest.raises(SystemExit):
779+
await _set_heat_status(client, FIRE_ID, "maybe")
780+
captured = capsys.readouterr()
781+
assert "Error" in captured.out
782+
783+
732784
# ---------------------------------------------------------------------------
733785
# _set_media_color
734786
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)