steamloop 1.2.0__tar.gz → 1.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {steamloop-1.2.0 → steamloop-1.2.2}/PKG-INFO +1 -1
  2. {steamloop-1.2.0 → steamloop-1.2.2}/pyproject.toml +1 -1
  3. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/__init__.py +1 -1
  4. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/cli.py +21 -6
  5. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop.egg-info/PKG-INFO +1 -1
  6. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_cli.py +25 -0
  7. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_dunder_main.py +1 -1
  8. {steamloop-1.2.0 → steamloop-1.2.2}/LICENSE +0 -0
  9. {steamloop-1.2.0 → steamloop-1.2.2}/README.md +0 -0
  10. {steamloop-1.2.0 → steamloop-1.2.2}/setup.cfg +0 -0
  11. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/__main__.py +0 -0
  12. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/certs.py +0 -0
  13. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/connection.py +0 -0
  14. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/const.py +0 -0
  15. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/exceptions.py +0 -0
  16. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/models.py +0 -0
  17. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop/py.typed +0 -0
  18. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop.egg-info/SOURCES.txt +0 -0
  19. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop.egg-info/dependency_links.txt +0 -0
  20. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop.egg-info/entry_points.txt +0 -0
  21. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop.egg-info/requires.txt +0 -0
  22. {steamloop-1.2.0 → steamloop-1.2.2}/src/steamloop.egg-info/top_level.txt +0 -0
  23. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_certs.py +0 -0
  24. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_connection.py +0 -0
  25. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_const.py +0 -0
  26. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_exceptions.py +0 -0
  27. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_models.py +0 -0
  28. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_pairing.py +0 -0
  29. {steamloop-1.2.0 → steamloop-1.2.2}/tests/test_protocol.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steamloop
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Local control for choochoo based thermostats
5
5
  Author-email: "J. Nick Koston" <nick@koston.org>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ requires = [ "setuptools>=77.0.3" ]
4
4
 
5
5
  [project]
6
6
  name = "steamloop"
7
- version = "1.2.0"
7
+ version = "1.2.2"
8
8
  description = "Local control for choochoo based thermostats"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  """Local control for thermostat devices over mTLS."""
2
2
 
3
- __version__ = "1.2.0"
3
+ __version__ = "1.2.2"
4
4
 
5
5
  from .connection import ThermostatConnection, load_pairing, save_pairing
6
6
  from .const import DEFAULT_PORT, FanMode, HoldType, ZoneMode
@@ -19,8 +19,8 @@ if TYPE_CHECKING:
19
19
 
20
20
  _LOGGER = logging.getLogger(__name__)
21
21
 
22
- _EHEAT_LABELS = {"": "N/A", "1": "On", "2": "Off"}
23
- _ACTIVE_LABELS = {"": "N/A", "1": "Inactive", "2": "Active"}
22
+ _EHEAT_LABELS = {"": "N/A", "0": "Off", "1": "On"}
23
+ _ACTIVE_LABELS = {"": "N/A", "0": "Inactive", "1": "Idle", "2": "Active"}
24
24
 
25
25
  _HOLD_MAP: dict[str, HoldType] = {
26
26
  "manual": HoldType.MANUAL,
@@ -86,14 +86,28 @@ def _print_help(active_zone: str) -> None:
86
86
  print(" quit Disconnect and exit")
87
87
 
88
88
 
89
+ def _valid_temp(temp: str) -> bool:
90
+ """Return True if temp parses as a number; print an error otherwise."""
91
+ try:
92
+ float(temp)
93
+ except ValueError:
94
+ print(f"Invalid temperature: {temp!r}. Expected a number.")
95
+ return False
96
+ return True
97
+
98
+
89
99
  def _cmd_heat(conn: ThermostatConnection, active_zone: str, temp: str) -> None:
90
100
  """Handle the heat command."""
101
+ if not _valid_temp(temp):
102
+ return
91
103
  print(f"Setting heat setpoint to {temp} (zone {active_zone})")
92
104
  conn.set_temperature_setpoint(active_zone, heat_setpoint=temp)
93
105
 
94
106
 
95
107
  def _cmd_cool(conn: ThermostatConnection, active_zone: str, temp: str) -> None:
96
108
  """Handle the cool command."""
109
+ if not _valid_temp(temp):
110
+ return
97
111
  print(f"Setting cool setpoint to {temp} (zone {active_zone})")
98
112
  conn.set_temperature_setpoint(active_zone, cool_setpoint=temp)
99
113
 
@@ -131,10 +145,11 @@ def _handle_command(
131
145
  _cmd_cool(conn, active_zone, parts[1])
132
146
 
133
147
  elif parts[0] == "setpoint" and len(parts) >= 3:
134
- print(f"Setting heat={parts[1]}, cool={parts[2]} (zone {active_zone})")
135
- conn.set_temperature_setpoint(
136
- active_zone, heat_setpoint=parts[1], cool_setpoint=parts[2]
137
- )
148
+ if _valid_temp(parts[1]) and _valid_temp(parts[2]):
149
+ print(f"Setting heat={parts[1]}, cool={parts[2]} (zone {active_zone})")
150
+ conn.set_temperature_setpoint(
151
+ active_zone, heat_setpoint=parts[1], cool_setpoint=parts[2]
152
+ )
138
153
 
139
154
  elif parts[0] == "hold" and len(parts) >= 2:
140
155
  ht = _HOLD_MAP.get(parts[1])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steamloop
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Local control for choochoo based thermostats
5
5
  Author-email: "J. Nick Koston" <nick@koston.org>
6
6
  License-Expression: Apache-2.0
@@ -101,6 +101,31 @@ def test_command_setpoint(capsys: pytest.CaptureFixture[str]) -> None:
101
101
  )
102
102
 
103
103
 
104
+ def test_command_heat_invalid_temp(capsys: pytest.CaptureFixture[str]) -> None:
105
+ """A non-numeric heat temp is rejected without calling the connection."""
106
+ conn = _make_mock_conn()
107
+ result = _handle_command(conn, "heat abc", ["heat", "abc"], "1")
108
+ assert result == "1"
109
+ conn.set_temperature_setpoint.assert_not_called()
110
+ assert "Invalid temperature" in capsys.readouterr().out
111
+
112
+
113
+ def test_command_cool_invalid_temp(capsys: pytest.CaptureFixture[str]) -> None:
114
+ """A non-numeric cool temp is rejected without calling the connection."""
115
+ conn = _make_mock_conn()
116
+ _handle_command(conn, "cool hot", ["cool", "hot"], "1")
117
+ conn.set_temperature_setpoint.assert_not_called()
118
+ assert "Invalid temperature" in capsys.readouterr().out
119
+
120
+
121
+ def test_command_setpoint_invalid_temp(capsys: pytest.CaptureFixture[str]) -> None:
122
+ """A bad value in setpoint rejects the whole command, no call is made."""
123
+ conn = _make_mock_conn()
124
+ _handle_command(conn, "setpoint 68 cold", ["setpoint", "68", "cold"], "1")
125
+ conn.set_temperature_setpoint.assert_not_called()
126
+ assert "Invalid temperature" in capsys.readouterr().out
127
+
128
+
104
129
  def test_command_hold() -> None:
105
130
  conn = _make_mock_conn()
106
131
  _handle_command(conn, "hold manual", ["hold", "manual"], "1")
@@ -7,7 +7,7 @@ import sys
7
7
 
8
8
 
9
9
  def test_can_run_as_python_module() -> None:
10
- result = subprocess.run( # noqa: S603
10
+ result = subprocess.run(
11
11
  [sys.executable, "-m", "steamloop", "--help"],
12
12
  check=True,
13
13
  capture_output=True,
File without changes
File without changes
File without changes
File without changes
File without changes