Skip to content

Commit e51e0c6

Browse files
authored
Merge pull request #4 from amitfin/master
swing support
2 parents f690b4c + 138e150 commit e51e0c6

File tree

3 files changed

+72
-23
lines changed

3 files changed

+72
-23
lines changed

pycoolmasternet_async/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .coolmasternet import CoolMasterNet
2+
from .coolmasternet import SWING_MODES

pycoolmasternet_async/coolmasternet.py

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,57 @@
33

44
_MODES = ["auto", "cool", "dry", "fan", "heat"]
55

6+
_SWING_CHAR_TO_NAME = {
7+
"a": "auto",
8+
"h": "horizontal",
9+
"3": "30",
10+
"4": "45",
11+
"6": "60",
12+
"v": "vertical",
13+
"x": "stop",
14+
}
15+
16+
_SWING_NAME_TO_CHAR = {value: key for key, value in _SWING_CHAR_TO_NAME.items()}
17+
18+
SWING_MODES = list(_SWING_CHAR_TO_NAME.values())
19+
620

721
class CoolMasterNet():
822
"""A connection to a coolmasternet bridge."""
9-
def __init__(self, host, port=10102, read_timeout=1):
23+
def __init__(self, host, port=10102, read_timeout=1, swing_support=False):
1024
"""Initialize this CoolMasterNet instance to connect to a particular
1125
host at a particular port."""
1226
self._host = host
1327
self._port = port
1428
self._read_timeout = read_timeout
29+
self._swing_support = swing_support
30+
self._concurrent_reads = asyncio.Semaphore(3)
1531

1632
async def _make_request(self, request):
1733
"""Send a request to the CoolMasterNet and returns the response."""
18-
reader, writer = await asyncio.open_connection(self._host, self._port)
34+
async with self._concurrent_reads:
35+
36+
reader, writer = await asyncio.open_connection(self._host, self._port)
1937

20-
try:
21-
prompt = await asyncio.wait_for(reader.readuntil(b">"), self._read_timeout)
22-
if prompt != b">":
23-
raise Exception("CoolMasterNet prompt not found")
38+
try:
39+
prompt = await asyncio.wait_for(reader.readuntil(b">"), self._read_timeout)
40+
if prompt != b">":
41+
raise Exception("CoolMasterNet prompt not found")
2442

25-
writer.write((request + "\n").encode("ascii"))
26-
response = await asyncio.wait_for(reader.readuntil(b"\n>"), self._read_timeout)
43+
writer.write((request + "\n").encode("ascii"))
44+
response = await asyncio.wait_for(reader.readuntil(b"\n>"), self._read_timeout)
2745

28-
data = response.decode("ascii")
29-
if data.endswith("\n>"):
30-
data = data[:-1]
46+
data = response.decode("ascii")
47+
if data.endswith("\n>"):
48+
data = data[:-1]
3149

32-
if data.endswith("OK\r\n"):
33-
data = data[:-4]
50+
if data.endswith("OK\r\n"):
51+
data = data[:-4]
3452

35-
return data
36-
finally:
37-
writer.close()
38-
await writer.wait_closed()
53+
return data
54+
finally:
55+
writer.close()
56+
await writer.wait_closed()
3957

4058
async def info(self):
4159
"""Get the general info the this CoolMasterNet."""
@@ -48,20 +66,30 @@ async def status(self):
4866
"""Return a list of CoolMasterNetUnit objects with current status."""
4967
status_lines = (await self._make_request("ls2")).strip().split("\r\n")
5068
return {
51-
line[0:6]:CoolMasterNetUnit(self, line[0:6], line)
52-
for line in status_lines
69+
key: unit
70+
for unit, key in await asyncio.gather(
71+
*(CoolMasterNetUnit.create(self, line[0:6], line) for line in status_lines))
5372
}
5473

5574

5675
class CoolMasterNetUnit():
5776
"""An immutable snapshot of a unit."""
58-
def __init__(self, bridge, unit_id, raw):
77+
def __init__(self, bridge, unit_id, raw, swing_raw):
5978
"""Initialize a unit snapshot."""
6079
self._raw = raw
80+
self._swing_raw = swing_raw
6181
self._unit_id = unit_id
6282
self._bridge = bridge
6383
self._parse()
6484

85+
@classmethod
86+
async def create(cls, bridge, unit_id, raw=None):
87+
if raw is None:
88+
raw = (await bridge._make_request(f"ls2 {unit_id}")).strip()
89+
swing_raw = ((await bridge._make_request(f"query {unit_id} s")).strip()
90+
if bridge._swing_support else "")
91+
return CoolMasterNetUnit(bridge, unit_id, raw, swing_raw), unit_id
92+
6593
def _parse(self):
6694
fields = re.split(r"\s+", self._raw.strip())
6795
if len(fields) != 9:
@@ -73,14 +101,14 @@ def _parse(self):
73101
self._temperature = float(fields[3][:-1])
74102
self._fan_speed = fields[4].lower()
75103
self._mode = fields[5].lower()
104+
self._swing = _SWING_CHAR_TO_NAME.get(self._swing_raw)
76105

77106
async def _make_unit_request(self, request):
78107
return await self._bridge._make_request(request.replace("UID", self._unit_id))
79108

80109
async def refresh(self):
81110
"""Refresh the data from CoolMasterNet and return it as a new instance."""
82-
status_line = (await self._make_unit_request("ls2 UID")).strip()
83-
return CoolMasterNetUnit(self._bridge, status_line[0:6], status_line)
111+
return (await CoolMasterNetUnit.create(self._bridge, self._unit_id))[0]
84112

85113
@property
86114
def unit_id(self):
@@ -112,6 +140,11 @@ def mode(self):
112140
"""The current mode (e.g. heat, cool)."""
113141
return self._mode
114142

143+
@property
144+
def swing(self):
145+
"""The current swing mode (e.g. horizontal)."""
146+
return self._swing
147+
115148
@property
116149
def temperature_unit(self):
117150
return self._temperature_unit
@@ -137,6 +170,21 @@ async def set_thermostat(self, value):
137170
await self._make_unit_request(f"temp UID {value}")
138171
return await self.refresh()
139172

173+
async def set_swing(self, value):
174+
"""Set the swing mode."""
175+
if not value in SWING_MODES:
176+
raise ValueError(
177+
f"Unrecognized swing mode {value}. Valid values: {', '.join(SWING_MODES)}"
178+
)
179+
180+
return_value = await self._make_unit_request(f"swing UID {_SWING_NAME_TO_CHAR[value]}")
181+
if return_value.startswith("Unsupported Feature"):
182+
raise ValueError(
183+
f"Unit {self._unit_id} doesn't support swing mode {value}."
184+
)
185+
186+
return await self.refresh()
187+
140188
async def turn_on(self):
141189
"""Turn a unit on."""
142190
await self._make_unit_request("on UID")

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
EMAIL = 'onfreund@gmail.com'
1919
AUTHOR = 'On Freund'
2020
REQUIRES_PYTHON = '>=3.7.0'
21-
VERSION = '0.1.3'
21+
VERSION = '0.1.4'
2222

2323
REQUIRED = []
2424

0 commit comments

Comments
 (0)