Skip to content

Commit ffab8f3

Browse files
committed
CU3 controllers now use the last known value for COOL and HEAT when controlling from OFF
1 parent 007ac8e commit ffab8f3

File tree

6 files changed

+89
-31
lines changed

6 files changed

+89
-31
lines changed

inelsmqtt/devices/__init__.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -240,15 +240,18 @@ def last_values(self) -> DeviceValue:
240240
Returns:
241241
DeviceValue: latest values in many formats
242242
"""
243-
val = self.__mqtt.last_value(self.__state_topic)
244-
245-
dev_value = DeviceValue(
246-
self.__device_type,
247-
self.__inels_type,
248-
self.__device_class,
249-
inels_value=val.decode() if val is not None else None, # type: ignore[attr-defined]
250-
)
251-
return dev_value
243+
try:
244+
if hasattr(self.__values.last_value.ha_value, "__dict__"):
245+
return self.__values.last_value
246+
raise AttributeError
247+
except AttributeError:
248+
val = self.__mqtt.last_value(self.__state_topic)
249+
return DeviceValue(
250+
self.__device_type,
251+
self.__inels_type,
252+
self.__device_class,
253+
inels_value=val.decode() if val is not None else None, # type: ignore[attr-defined]
254+
)
252255

253256
@property
254257
def mqtt(self) -> InelsMqtt:
@@ -267,7 +270,8 @@ def __get_value(self, val: Any) -> DeviceValue:
267270
self.__inels_type,
268271
self.__device_class,
269272
inels_value=(val.decode() if val is not None else None),
270-
last_value=self.last_values,
273+
# last_value=self.last_values,
274+
last_value=self.__values, # because it is already the last value at this moment
271275
)
272276
self.__state = dev_value.ha_value
273277
self.__values = dev_value
@@ -298,7 +302,8 @@ def set_ha_value(self, value: Any) -> bool:
298302
self.__inels_type,
299303
self.__device_class,
300304
ha_value=value,
301-
last_value=self.__state,
305+
# last_value=self.__state,
306+
last_value=self.__values, # because the last_value will be accessible
302307
)
303308

304309
self.__state = dev.ha_value

inelsmqtt/protocols/cu3.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,7 @@ def create_ha_value_object(cls, device_value: DeviceValue) -> Any:
11871187
card_id_int = int(card_id, 16)
11881188
# if card removed before
11891189
if card_id_int == 0 and device_value.last_value is not None:
1190-
card_id = device_value.last_value.card_id
1190+
card_id = device_value.last_value.ha_value.card_id
11911191

11921192
interface = [state[0] == "1", state[12] == "1", state[10] == "1"]
11931193

@@ -2041,6 +2041,9 @@ def create_command_payload(out1: int = 0, out2: int = 0) -> str:
20412041

20422042
@classmethod
20432043
def create_ha_value_object(cls, device_value: DeviceValue) -> Any:
2044+
last_known_required = None
2045+
last_known_required_cool = None
2046+
20442047
temp_current: float = int(
20452048
trim_inels_status_values(device_value.inels_status_value, cls.DATA, CURRENT_TEMP, ""), 16
20462049
)
@@ -2063,8 +2066,15 @@ def create_ha_value_object(cls, device_value: DeviceValue) -> Any:
20632066
)
20642067
if temp_required_heat == 0x7FFFFFFB:
20652068
temp_required_heat = 0
2069+
if hasattr(device_value.last_value, "ha_value"):
2070+
last_known_required = device_value.last_value.ha_value.climate_controller.last_known_required
20662071
else:
20672072
temp_required_heat /= 100
2073+
if temp_required_heat == 0 and hasattr(device_value.last_value, "ha_value"):
2074+
last_known_required = device_value.last_value.ha_value.climate_controller.last_known_required
2075+
else:
2076+
if temp_required_heat != 0:
2077+
last_known_required = temp_required_heat
20682078

20692079
temp_critical_min = (
20702080
int(
@@ -2080,8 +2090,15 @@ def create_ha_value_object(cls, device_value: DeviceValue) -> Any:
20802090
)
20812091
if temp_required_cool == 0x7FFFFFFB:
20822092
temp_required_cool = 0
2093+
if hasattr(device_value.last_value, "ha_value"):
2094+
last_known_required_cool = device_value.last_value.ha_value.climate_controller.last_known_required_cool
20832095
else:
20842096
temp_required_cool /= 100
2097+
if temp_required_cool == 0 and hasattr(device_value.last_value, "ha_value"):
2098+
last_known_required_cool = device_value.last_value.ha_value.climate_controller.last_known_required_cool
2099+
else:
2100+
if temp_required_cool != 0:
2101+
last_known_required_cool = temp_required_cool
20852102

20862103
temp_correction = (
20872104
int(trim_inels_status_values(device_value.inels_status_value, cls.DATA, TEMP_CORRECTION, ""), 16) / 100
@@ -2142,7 +2159,9 @@ def create_ha_value_object(cls, device_value: DeviceValue) -> Any:
21422159
climate_controller=new_object(
21432160
current=temp_current, # current_temperature
21442161
required=temp_required_heat, # target_temperature / target_temperature_high
2162+
last_known_required=last_known_required,
21452163
required_cool=temp_required_cool, # target_temperature_low
2164+
last_known_required_cool=last_known_required_cool,
21462165
climate_mode=climate_mode, # hvac_mode: Off/Heat_cool/Heat/Cool
21472166
# Off -> controller is turned off
21482167
# Heat_cool -> follow temp range
@@ -2173,14 +2192,6 @@ def create_inels_set_value(cls, device_value: DeviceValue) -> str:
21732192
critical_temp = [int(x, 16) for x in break_into_bytes(f"{int(cc.critical_temp * 100):08X}")]
21742193
critical_temp.reverse()
21752194

2176-
manual_temp = [int(x, 16) for x in break_into_bytes(f"{int((cc.required + cc.correction_temp) * 100):08X}")]
2177-
manual_temp.reverse()
2178-
2179-
manual_cool_temp = [
2180-
int(x, 16) for x in break_into_bytes(f"{int((cc.required_cool + cc.correction_temp) * 100):08X}")
2181-
]
2182-
manual_cool_temp.reverse()
2183-
21842195
plan_in = 0
21852196
if cc.public_holiday > 0:
21862197
plan_in = 128 # 0x80
@@ -2200,6 +2211,24 @@ def create_inels_set_value(cls, device_value: DeviceValue) -> str:
22002211
else:
22012212
byte18 = 1
22022213

2214+
required_heat = cc.required
2215+
required_cool = cc.required_cool
2216+
if (required_heat == 0 or required_cool == 0) and manual_in == 7 and (byte18 == 1 or byte18 == 3):
2217+
try:
2218+
climate_controller = device_value.last_value.ha_value.climate_controller
2219+
required_heat = climate_controller.last_known_required or required_heat
2220+
required_cool = climate_controller.last_known_required_cool or required_cool
2221+
except AttributeError:
2222+
pass
2223+
2224+
manual_temp = [int(x, 16) for x in break_into_bytes(f"{int((required_heat + cc.correction_temp) * 100):08X}")]
2225+
manual_temp.reverse()
2226+
2227+
manual_cool_temp = [
2228+
int(x, 16) for x in break_into_bytes(f"{int((required_cool + cc.correction_temp) * 100):08X}")
2229+
]
2230+
manual_cool_temp.reverse()
2231+
22032232
set_val = current_temp + critical_temp + manual_temp + manual_cool_temp
22042233
set_val += [plan_in, manual_in, byte18]
22052234

inelsmqtt/protocols/elanrf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def create_ha_value_object(cls, device_value: DeviceValue) -> Any:
225225
# So as to continue driving it down if it aisn't closed
226226
# and continue opening it if it isn't open
227227
if shutter_val not in [Shutter_state.Open, Shutter_state.Closed]:
228-
shutter_val = device_value.last_value.shutters[0].state
228+
shutter_val = device_value.last_value.ha_value.shutters[0].state
229229

230230
shutters: List[Shutter] = []
231231
shutters.append(Shutter(state=shutter_val, is_closed=shutter_val == Shutter_state.Closed))
@@ -783,7 +783,7 @@ def create_ha_value_object(cls, device_value: DeviceValue) -> Any:
783783
shutter_val = Shutter_state.Open
784784

785785
if (device_value.last_value is not None) and (shutter_val not in [Shutter_state.Open, Shutter_state.Closed]):
786-
shutter_val = device_value.last_value.shutters_with_pos[0].state
786+
shutter_val = device_value.last_value.ha_value.shutters_with_pos[0].state
787787

788788
shutters_with_pos.append(
789789
Shutter_pos(

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
setup(
66
name="elkoep-mqtt",
7-
version="0.2.33.beta.14",
7+
version="0.2.33.beta.15",
88
url="https://github.com/epdevlab/elkoep-mqtt",
99
license="MIT",
1010
author="Elko EP s.r.o.",

tests/test_device.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def test_device_callbacks(device):
113113

114114

115115
def test_device_callback_availability(device):
116-
device.mqtt.messages.return_value.get.side_effect = [b"02\n01\n", b"02\n01\n"]
116+
device.mqtt.messages.return_value.get.side_effect = [b"02\n01\n", b"02\n00\n"]
117117

118118
callback = MagicMock()
119119
device.add_ha_callback("simple_relay", 0, callback)

tests/test_device_value.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ def test_create_ha_value_object_closed(self, device_value_closed):
559559
assert device_value_closed.ha_value.shutters_with_pos[0].state == 1
560560

561561
def test_create_ha_value_object_last_value(self, device_value_open):
562-
device_value = self.create_device_value(inels_value="03\n01\n64\n", last_value=device_value_open.ha_value)
562+
device_value = self.create_device_value(inels_value="03\n01\n64\n", last_value=device_value_open)
563563
assert device_value.ha_value.shutters_with_pos[0].state == 0
564564

565565
def test_format_inels_set_value_open(self, device_value_closed):
@@ -2003,15 +2003,15 @@ class Test_CU_DEVICE_TYPE_166(BaseDeviceTestClass):
20032003
@pytest.fixture
20042004
def device_value(self):
20052005
return self.create_device_value(
2006-
inels_value="3F\n0A\n00\n00\nFB\nFF\nFF\n7F\nFB\nFF\nFF\n7F\n00\n00\n00\n00\n00\n00\n00\n00\nFB\nFF\nFF\n7F\n00\n00\n00\n00\n00\n00\n00\n"
2006+
inels_value="DD\n0A\n00\n00\nFB\nFF\nFF\n7F\n80\n0C\n00\n00\n88\n13\n00\n00\nBC\n02\n00\n00\n00\n00\n00\n00\n00\n00\n00\n00\n00\n00\n05\n"
20072007
)
20082008

20092009
def test_create_ha_value_object(self, device_value):
2010-
assert device_value.ha_value.climate_controller.current == 26.23
2011-
assert device_value.ha_value.climate_controller.required == 0
2010+
assert device_value.ha_value.climate_controller.current == 27.81
2011+
assert device_value.ha_value.climate_controller.required == 32.0
20122012
assert device_value.ha_value.climate_controller.required_cool == 0
2013-
assert device_value.ha_value.climate_controller.climate_mode == 0
2014-
assert device_value.ha_value.climate_controller.current_action == 0
2013+
assert device_value.ha_value.climate_controller.climate_mode == 1
2014+
assert device_value.ha_value.climate_controller.current_action == 2
20152015
assert device_value.ha_value.climate_controller.critical_temp == 21474836.43
20162016
assert device_value.ha_value.climate_controller.correction_temp == 0.0
20172017
assert device_value.ha_value.climate_controller.public_holiday == 0
@@ -2025,7 +2025,31 @@ def test_format_inels_set_value_all_to_max(self, device_value):
20252025
)
20262026
assert (
20272027
device_value.inels_set_value
2028-
== "3F\n0A\n00\n00\nFB\nFF\nFF\n7F\n00\n00\n00\n00\n00\n00\n00\n00\n00\n07\n00\n"
2028+
== "DD\n0A\n00\n00\nFB\nFF\nFF\n7F\n80\n0C\n00\n00\n00\n00\n00\n00\n00\n07\n01\n"
2029+
)
2030+
2031+
def test_create_ha_value_required_temp_after_hca_turned_off(self, device_value):
2032+
assert device_value.ha_value.climate_controller.required == 32.0
2033+
assert device_value.ha_value.climate_controller.last_known_required == 32.0
2034+
2035+
hca_turned_off_device_value = self.create_device_value(
2036+
inels_value="DD\n0A\n00\n00\nFB\nFF\nFF\n7F\nFB\nFF\nFF\n7F\n88\n13\n00\n00\nBC\n02\n00\n00\nFB\nFF\nFF\n7F\n00\n00\n00\n00\n00\n00\n00\n",
2037+
last_value=device_value,
2038+
)
2039+
2040+
assert hca_turned_off_device_value.ha_value.climate_controller.required == 0
2041+
assert hca_turned_off_device_value.ha_value.climate_controller.last_known_required == 32.0
2042+
2043+
assert hca_turned_off_device_value.last_value.ha_value.climate_controller.last_known_required == 32.0
2044+
2045+
hca_turned_on_device_value = self.create_device_value(
2046+
inels_value="DD\n0A\n00\n00\nFB\nFF\nFF\n7F\n00\n00\n00\n00\n88\n13\n00\n00\nBC\n02\n00\n00\n00\n00\n00\n00\n00\n00\n00\n00\n00\n00\n05\n",
2047+
last_value=hca_turned_off_device_value,
2048+
)
2049+
# write back last known value: 80\n0C\n00\n00 == 32.0
2050+
assert (
2051+
hca_turned_on_device_value.inels_set_value
2052+
== "DD\n0A\n00\n00\nFB\nFF\nFF\n7F\n80\n0C\n00\n00\n00\n00\n00\n00\n00\n07\n01\n"
20292053
)
20302054

20312055

0 commit comments

Comments
 (0)