nilan-proxy 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,11 @@
1
+ from .nilan_proxy import ( NilanProxy, NilanProxyConnectionErrorType )
2
+ from .models import ( NilanProxyDatapointKey, NilanProxySetpointKey, NilanProxyUnits )
3
+
4
+ __version__ = "1.0.0"
5
+ __all__ = [
6
+ "NilanProxy",
7
+ "NilanProxyConnectionErrorType",
8
+ "NilanProxyDatapointKey",
9
+ "NilanProxySetpointKey",
10
+ "NilanProxyUnits",
11
+ ]
nilan_proxy/const.py ADDED
@@ -0,0 +1,6 @@
1
+ SOCKET_TIMEOUT = 1 # How long to wait for socket data, before moving on to other listen thread tasks.
2
+ SOCKET_MAXSIZE = 512
3
+ DATAPOINT_UPDATEINTERVAL = 10 # Seconds since last datapoint update to trigger new update
4
+ SETPOINT_UPDATEINTERVAL = 180 # Seconds since last setpoint update to trigger new update
5
+ SECONDS_UNTILRECONNECT = 20 # Seconds with no response to try reconnecting
6
+ DISCOVERY_PORT = 5570
@@ -0,0 +1,29 @@
1
+ from .basemodel import ( NilanProxyBaseModel, NilanProxyDatapoint, NilanProxyDatapointKey, NilanProxySetpoint, NilanProxySetpointKey, NilanProxyUnits )
2
+ from .optima314 import NilanProxyOptima314
3
+ from .optima312 import NilanProxyOptima312
4
+ from .optima301 import NilanProxyOptima301
5
+ from .optima270 import NilanProxyOptima270
6
+ from .optima260 import NilanProxyOptima260
7
+ from .optima251 import NilanProxyOptima251
8
+ from .optima250 import NilanProxyOptima250
9
+ from .cts602 import NilanProxyCTS602
10
+ from .cts602light import NilanProxyCTS602Light
11
+ from .cts400 import NilanProxyCTS400
12
+ __all__ = [
13
+ "NilanProxyBaseModel",
14
+ "NilanProxyDatapoint",
15
+ "NilanProxyDatapointKey",
16
+ "NilanProxySetpoint",
17
+ "NilanProxySetpointKey",
18
+ "NilanProxyUnits",
19
+ "NilanProxyOptima314",
20
+ "NilanProxyOptima312",
21
+ "NilanProxyOptima301",
22
+ "NilanProxyOptima270",
23
+ "NilanProxyOptima260",
24
+ "NilanProxyOptima251",
25
+ "NilanProxyOptima250",
26
+ "NilanProxyCTS602",
27
+ "NilanProxyCTS602Light",
28
+ "NilanProxyCTS400"
29
+ ]
@@ -0,0 +1,435 @@
1
+ from collections.abc import Callable
2
+ from enum import Enum
3
+ from typing import Dict, List, TypedDict, NotRequired
4
+
5
+
6
+ class NilanProxyDatapointKey(Enum):
7
+ ALARM_1_CODE = "alarm_1_code"
8
+ ALARM_1_INFO = "alarm_1_info"
9
+ ALARM_2_CODE = "alarm_2_code"
10
+ ALARM_2_INFO = "alarm_2_info"
11
+ ALARM_3_CODE = "alarm_3_code"
12
+ ALARM_3_INFO = "alarm_3_info"
13
+ ALARM_STATUS = "alarm_status"
14
+ """Indicates if an alarm is active"""
15
+ ALARM_BITS = "alarm_bits"
16
+ BYPASS_ACTIVE = "bypass_active"
17
+ CO2_LEVEL = "co2_level"
18
+ DEFROST_ACTIVE = "defrost_active"
19
+ DEFROST_TIME_AGO = "defrost_time_ago"
20
+ """
21
+ (default=days)
22
+ """
23
+ FAN_DUTYCYCLE_EXTRACT = "fan_dutycycle_extract"
24
+ FAN_DUTYCYCLE_SUPPLY = "fan_dutycycle_supply"
25
+ FAN_LEVEL_CURRENT = "fan_level_current"
26
+ """The fan level currently active"""
27
+ FAN_LEVEL_EXTRACT = "fan_level_extract"
28
+ FAN_LEVEL_SUPPLY = "fan_level_supply"
29
+ FAN_RPM_EXTRACT = "fan_rpm_extract"
30
+ FAN_RPM_SUPPLY = "fan_rpm_supply"
31
+ FILTER_REPLACE_TIME_AGO = "filter_replace_time_ago"
32
+ """
33
+ (default=days)
34
+ """
35
+ FILTER_REPLACE_TIME_REMAIN = "filter_replace_time_remain"
36
+ """
37
+ (default=days)
38
+ """
39
+ FILTER_OK = "filter_ok"
40
+ HUMIDITY = "humidity"
41
+ HUMIDITY_AVG = "humidity_average"
42
+ HUMIDITY_HIGH_ACTIVE = "humidity_high_active"
43
+ HUMIDITY_HIGH_LEVEL = "humidity_high_level"
44
+ HUMIDITY_HIGH_LEVEL_TIME = "humidity_high_level_time"
45
+ """Time the high humidity functionality has been active"""
46
+ SACRIFICIAL_ANODE_OK = "sacrificial_anode_ok"
47
+ STATE_CODE = "state_code"
48
+ TEMP_CONDENSER = "temp_condenser"
49
+ TEMP_EVAPORATOR = "temp_evaporator"
50
+ TEMP_EXHAUST = "temp_exhaust"
51
+ TEMP_EXTRACT = "temp_extract"
52
+ TEMP_HOTWATER_BOTTOM = "temp_hotwater_bottom"
53
+ TEMP_HOTWATER_TOP = "temp_hotwater_top"
54
+ TEMP_OUTSIDE = "temp_outside"
55
+ TEMP_ROOM = "temp_room"
56
+ TEMP_SUPPLY = "temp_supply"
57
+ VOC_LEVEL = "voc_level"
58
+ WINTER_MODE_ACTIVE = "winter_mode_active"
59
+
60
+ UNKNOWN_VALUE_1 = "unknown_value_1"
61
+
62
+
63
+ class NilanProxySetpointKey(Enum):
64
+ """
65
+ Setpoints that can be read/written.
66
+
67
+ If a key has double underscore '__' followed by a number, it contains preset values that can be different from the read value.
68
+ """
69
+ ALARM_RESET = "alarm_reset"
70
+
71
+ ANTILEGIONELLA_DAY = "antilegionella_day"
72
+ """
73
+ Day of the week the legionella treatment is performed.
74
+
75
+ Typically this is used when a hot water tank is installed.
76
+
77
+ Values:
78
+ - 0: "OFF",
79
+ - 1: "+0",
80
+ - 2: "+1",
81
+ - 3: "+2",
82
+ - 4: "+3",
83
+ - 5: "+4",
84
+ - 6: "+5",
85
+ - 7: "+7",
86
+ - 8: "+10"
87
+ """
88
+ BOOST_ENABLE = "boost_enable"
89
+ """Enable the boost function for the unit."""
90
+ BOOST_TIME = "boost_time"
91
+ """Time the boost function will run when triggered. (default=minutes)."""
92
+ CO2_THRESHOLD = "co2_threshold"
93
+ COOLING_ENABLE = "cooling_enable"
94
+ """Enable the cooling function for the unit.
95
+
96
+ Typically this will be active cooling, bypass has its own key.
97
+ """
98
+ COMPRESSOR_PRIORITY = "compressor_priority"
99
+ """Priority for the compressor.
100
+ """
101
+ DEFROST_BREAK_TIME = "defrost_break_time"
102
+ DEFROST_MAX_TIME = "defrost_max_time"
103
+ ENABLE = "enable"
104
+ """Enable the unit."""
105
+ FAN_LEVEL = "fan_level"
106
+ """Select the fan level.
107
+ Typically this activates a preset configuration.
108
+ """
109
+ FAN_LEVEL_LOW_HUMIDITY = "fan_level_low_humidity"
110
+ FAN_LEVEL_HIGH_CO2 = "fan_level_high_co2"
111
+ FAN_LEVEL_HIGH_HUMIDITY = "fan_level_high_humidity"
112
+ FAN_LEVEL_HIGH_HUMIDITY_TIME = "fan_level_high_humidity_time"
113
+ FAN_LEVEL1_EXTRACT_PRESET = "fan_level1_extract_preset"
114
+ FAN_LEVEL1_SUPPLY_PRESET = "fan_level1_supply_preset"
115
+ FAN_LEVEL2_EXTRACT_PRESET = "fan_level2_extract_preset"
116
+ FAN_LEVEL2_SUPPLY_PRESET = "fan_level2_supply_preset"
117
+ FAN_LEVEL3_EXTRACT_PRESET = "fan_level3_extract_preset"
118
+ FAN_LEVEL3_SUPPLY_PRESET = "fan_level3_supply_preset"
119
+ FAN_LEVEL4_EXTRACT_PRESET = "fan_level4_extract_preset"
120
+ FAN_LEVEL4_SUPPLY_PRESET = "fan_level4_supply_preset"
121
+ FILTER_REPLACE_INTERVAL = "filter_replace_interval"
122
+ """Time interval between filter replacements/maintenance (default=days)"""
123
+ FILTER_REPLACE_RESET = "filter_replace_reset"
124
+ """Resets/restarts the timer for filter replacements/maintenance."""
125
+ HUMIDITY_CONTROL_ENABLE = "humidity_control_enable"
126
+ """Enable the humidity function for the unit.
127
+
128
+ Typically this will be extra fan level when showering or alike.
129
+ """
130
+ HUMIDITY_LOW_THRESHOLD = "humidity_low_threshold"
131
+ PREHEAT_ENABLE = "preheat_enable"
132
+ """Enable the preheat function for the unit.
133
+
134
+ Typically this will be a heating element for the outside air.
135
+ """
136
+ PREHEAT_CYCLE_TIME = "preheat_cycle_time"
137
+ PREHEAT_PID_P = "preheat_pid_p"
138
+ PREHEAT_PID_I = "preheat_pid_i"
139
+ PREHEAT_PID_D = "preheat_pid_d"
140
+ REHEAT_ENABLE = "reheat_enable"
141
+ """Enable the prehet function for the unit.
142
+
143
+ Typically this will be a heating element for the extract air.
144
+ """
145
+ REHEAT_CYCLE_TIME = "reheat_cycle_time"
146
+ REHEAT_PID_P = "reheat_pid_p"
147
+ REHEAT_PID_I = "reheat_pid_i"
148
+ REHEAT_PID_D = "reheat_pid_d"
149
+ TEMP_BYPASS_OPEN_OFFSET = "temp_bypass_open_offset"
150
+ """Temperature offset for when the bypass function.
151
+
152
+ Typically the bypass function will be a passive function to keep indoor temperature near the target.
153
+ """
154
+ TEMP_COOLING_START_OFFSET = "temp_cooling_start_offset"
155
+ """Temperature increase amount before cooling is started.
156
+
157
+ Values:
158
+ - 0: "OFF",
159
+ - 1: "+0",
160
+ - 2: "+1",
161
+ - 3: "+2",
162
+ - 4: "+3",
163
+ - 5: "+4",
164
+ - 6: "+5",
165
+ - 7: "+7",
166
+ - 8: "+10"
167
+ """
168
+ TEMP_DEFROST_LOW_THRESHOLD = "temp_defrost_low_threshold"
169
+ """Temperature below this will start defrost function."""
170
+ TEMP_DEFROST_HIGH_THRESHOLD = "temp_defrost_high_threshold"
171
+ """Temperature above this will stop defrost function."""
172
+ TEMP_HOTWATER = "temp_hotwater"
173
+ """Temperature setpoint for the hot water.
174
+
175
+ Typically this is used when a hot water container is installed.
176
+
177
+ This is the main heating element.
178
+ """
179
+ TEMP_HOTWATER_BOOST = "temp_hotwater_boost"
180
+ """Temperature boost setpoint for the hot water.
181
+
182
+ Typically this is used when a hot water container is installed.
183
+
184
+ This is the auxiliary (boosting) heating element.
185
+ """
186
+ TEMP_REGULATION_DEAD_BAND = "temp_regulation_dead_band"
187
+ """Temperature dead band for regulation to prevent mode switching oscillations."""
188
+ TEMP_REHEAT_OFFSET = "temp_reheat_offset"
189
+ """Temperature offset relative to indoor for the reheating."""
190
+ TEMP_TARGET = "temp_target"
191
+ """Temperature setpoint for the home."""
192
+ TEMP_SUMMER_SUPPLY_MAX = "temp_summer_supply_max"
193
+ """Temperature limit for summer supply air.
194
+
195
+ Typically this is used when a heater is heating the air.
196
+ """
197
+ TEMP_SUMMER_SUPPLY_MIN = "temp_summer_supply_min"
198
+ """Temperature limit for summer supply air.
199
+
200
+ Typically this is used when a heater is heating the air.
201
+ """
202
+ TEMP_SUPPLY_MAX = "temp_supply_max"
203
+ TEMP_SUPPLY_MIN = "temp_supply_min"
204
+ TEMP_WINTER_SUPPLY_MAX = "temp_winter_supply_max"
205
+ """Temperature limit for winter supply air.
206
+
207
+ Typically this is used when a heater is heating the air.
208
+ """
209
+ TEMP_WINTER_SUPPLY_MIN = "temp_winter_supply_min"
210
+ """Temperature limit for winter supply air.
211
+
212
+ Typically this is used when a heater is heating the air.
213
+ """
214
+ TEMP_WINTER_MODE_THRESHOLD = "temp_winter_mode_threshold"
215
+ """Temperature for switching between summer and winter modes.
216
+
217
+ - When outside temperature is above, summer mode is active.
218
+ - When outside temperature is below, winter mode is active.
219
+ """
220
+ VOC_THRESHOLD = "voc_threshold"
221
+
222
+
223
+ class NilanProxyDatapoint(TypedDict):
224
+ divider: NotRequired[int]
225
+ """Applied to the register value in the order: 1: divider, 2: offset, 3: modifier"""
226
+ offset: NotRequired[int]
227
+ """Applied to the register value in the order: 1: divider, 2: offset, 3: modifier"""
228
+ read_address: int
229
+ read_modifier: NotRequired[Callable[[float|int], float|int]]
230
+ """Modifier applied to value after it has been parsed by the system. can be used to alter hours to days etc. or round floating values
231
+
232
+ Applied to the register value in the order: 1: divider, 2: offset, 3: modifier"""
233
+ read_obj: NotRequired[int]
234
+ """default is 0"""
235
+ signed: bool
236
+ """indication of the data being signed or unsigned"""
237
+
238
+ class NilanProxySetpoint(NilanProxyDatapoint):
239
+ max: int
240
+ """max value in the register"""
241
+ min: int
242
+ """min value in the register"""
243
+ step: NotRequired[int]
244
+ """step size in register value, if unset will default to the divider"""
245
+ write_address: int
246
+ write_modifier: NotRequired[Callable[[float|int], float|int]]
247
+ """Modifier applied to value before it has been parsed back to register type. can be used to alter hours to days etc. or round floating values"""
248
+ write_obj: NotRequired[int]
249
+ """default is 0"""
250
+
251
+ class NilanProxyPointConfig(TypedDict):
252
+ unit_of_measurement: str|None
253
+ read: bool
254
+
255
+ class NilanProxyUnits:
256
+ SECONDS = "seconds"
257
+ MINUTES = "minutes"
258
+ HOURS = "hours"
259
+ DAYS = "days"
260
+ MONTHS = "months"
261
+ YEARS = "years"
262
+ CELSIUS = "celsius"
263
+ BOOL = "bool"
264
+ BITMASK = "bitmask"
265
+ PPM = "ppm"
266
+ """CONCENTRATION PARTS PER MILLION"""
267
+ RPM = "rpm"
268
+ """REVOLUTIONS PER MINUTE"""
269
+ # INT = "int"
270
+ # FLOAT = "float"
271
+ PCT = "percent"
272
+ TEXT = "text"
273
+ UNDEFINED = None
274
+
275
+ DEFAULT_CONFIGS:Dict[NilanProxyDatapointKey|NilanProxySetpointKey, NilanProxyPointConfig] = {
276
+ NilanProxyDatapointKey.ALARM_1_CODE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
277
+ NilanProxyDatapointKey.ALARM_1_INFO: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.TEXT, read=True),
278
+ NilanProxyDatapointKey.ALARM_2_CODE:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
279
+ NilanProxyDatapointKey.ALARM_2_INFO: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.TEXT, read=True),
280
+ NilanProxyDatapointKey.ALARM_3_CODE:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
281
+ NilanProxyDatapointKey.ALARM_3_INFO: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.TEXT, read=True),
282
+ NilanProxyDatapointKey.ALARM_STATUS:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
283
+ NilanProxyDatapointKey.ALARM_BITS:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BITMASK, read=True),
284
+ NilanProxyDatapointKey.BYPASS_ACTIVE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
285
+ NilanProxyDatapointKey.CO2_LEVEL: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PPM, read=True),
286
+ NilanProxyDatapointKey.DEFROST_ACTIVE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
287
+ NilanProxyDatapointKey.DEFROST_TIME_AGO:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.DAYS, read=True),
288
+ NilanProxyDatapointKey.FAN_DUTYCYCLE_EXTRACT: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
289
+ NilanProxyDatapointKey.FAN_DUTYCYCLE_SUPPLY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
290
+ NilanProxyDatapointKey.FAN_LEVEL_CURRENT: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
291
+ NilanProxyDatapointKey.FAN_LEVEL_EXTRACT: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
292
+ NilanProxyDatapointKey.FAN_LEVEL_SUPPLY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
293
+ NilanProxyDatapointKey.FAN_RPM_EXTRACT: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.RPM, read=True),
294
+ NilanProxyDatapointKey.FAN_RPM_SUPPLY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.RPM, read=True),
295
+ NilanProxyDatapointKey.FILTER_REPLACE_TIME_AGO: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.DAYS, read=True),
296
+ NilanProxyDatapointKey.FILTER_REPLACE_TIME_REMAIN: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.DAYS, read=True),
297
+ NilanProxyDatapointKey.FILTER_OK: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
298
+ NilanProxyDatapointKey.HUMIDITY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
299
+ NilanProxyDatapointKey.HUMIDITY_HIGH_LEVEL: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
300
+ NilanProxyDatapointKey.HUMIDITY_HIGH_LEVEL_TIME: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.MINUTES, read=True),
301
+ NilanProxyDatapointKey.HUMIDITY_AVG: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
302
+ NilanProxyDatapointKey.HUMIDITY_HIGH_ACTIVE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
303
+ NilanProxyDatapointKey.SACRIFICIAL_ANODE_OK: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
304
+ NilanProxyDatapointKey.STATE_CODE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
305
+ NilanProxyDatapointKey.TEMP_CONDENSER:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
306
+ NilanProxyDatapointKey.TEMP_EVAPORATOR:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
307
+ NilanProxyDatapointKey.TEMP_EXHAUST:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
308
+ NilanProxyDatapointKey.TEMP_EXTRACT: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
309
+ NilanProxyDatapointKey.TEMP_HOTWATER_BOTTOM: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
310
+ NilanProxyDatapointKey.TEMP_HOTWATER_TOP: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
311
+ NilanProxyDatapointKey.TEMP_OUTSIDE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
312
+ NilanProxyDatapointKey.TEMP_ROOM: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
313
+ NilanProxyDatapointKey.TEMP_SUPPLY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
314
+ NilanProxyDatapointKey.VOC_LEVEL: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PPM, read=True),
315
+ NilanProxyDatapointKey.WINTER_MODE_ACTIVE:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
316
+
317
+ NilanProxyDatapointKey.UNKNOWN_VALUE_1:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
318
+
319
+ NilanProxySetpointKey.ALARM_RESET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=False),
320
+ NilanProxySetpointKey.ANTILEGIONELLA_DAY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
321
+ NilanProxySetpointKey.BOOST_ENABLE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
322
+ NilanProxySetpointKey.BOOST_TIME: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.MINUTES, read=True),
323
+ NilanProxySetpointKey.CO2_THRESHOLD: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PPM, read=True),
324
+ NilanProxySetpointKey.COOLING_ENABLE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
325
+ NilanProxySetpointKey.COMPRESSOR_PRIORITY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
326
+ NilanProxySetpointKey.DEFROST_MAX_TIME: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.MINUTES, read=True),
327
+ NilanProxySetpointKey.DEFROST_BREAK_TIME: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.MINUTES, read=True),
328
+ NilanProxySetpointKey.ENABLE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
329
+ NilanProxySetpointKey.FAN_LEVEL: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
330
+ NilanProxySetpointKey.FAN_LEVEL_LOW_HUMIDITY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
331
+ NilanProxySetpointKey.FAN_LEVEL_HIGH_CO2: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
332
+ NilanProxySetpointKey.FAN_LEVEL_HIGH_HUMIDITY: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
333
+ NilanProxySetpointKey.FAN_LEVEL_HIGH_HUMIDITY_TIME: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.MINUTES, read=True),
334
+ NilanProxySetpointKey.FAN_LEVEL1_EXTRACT_PRESET:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
335
+ NilanProxySetpointKey.FAN_LEVEL1_SUPPLY_PRESET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
336
+ NilanProxySetpointKey.FAN_LEVEL2_EXTRACT_PRESET:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
337
+ NilanProxySetpointKey.FAN_LEVEL2_SUPPLY_PRESET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
338
+ NilanProxySetpointKey.FAN_LEVEL3_EXTRACT_PRESET:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
339
+ NilanProxySetpointKey.FAN_LEVEL3_SUPPLY_PRESET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
340
+ NilanProxySetpointKey.FAN_LEVEL4_EXTRACT_PRESET:NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
341
+ NilanProxySetpointKey.FAN_LEVEL4_SUPPLY_PRESET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
342
+ NilanProxySetpointKey.FILTER_REPLACE_INTERVAL: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.DAYS, read=True),
343
+ NilanProxySetpointKey.FILTER_REPLACE_RESET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=False),
344
+ NilanProxySetpointKey.HUMIDITY_CONTROL_ENABLE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
345
+ NilanProxySetpointKey.HUMIDITY_LOW_THRESHOLD: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PCT, read=True),
346
+ NilanProxySetpointKey.PREHEAT_ENABLE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
347
+ NilanProxySetpointKey.PREHEAT_CYCLE_TIME: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.SECONDS, read=True),
348
+ NilanProxySetpointKey.PREHEAT_PID_D: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
349
+ NilanProxySetpointKey.PREHEAT_PID_I: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
350
+ NilanProxySetpointKey.PREHEAT_PID_P: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
351
+ NilanProxySetpointKey.REHEAT_ENABLE: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.BOOL, read=True),
352
+ NilanProxySetpointKey.REHEAT_CYCLE_TIME: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.SECONDS, read=True),
353
+ NilanProxySetpointKey.REHEAT_PID_D: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
354
+ NilanProxySetpointKey.REHEAT_PID_I: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
355
+ NilanProxySetpointKey.REHEAT_PID_P: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.UNDEFINED, read=True),
356
+ NilanProxySetpointKey.TEMP_BYPASS_OPEN_OFFSET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
357
+ NilanProxySetpointKey.TEMP_COOLING_START_OFFSET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
358
+ NilanProxySetpointKey.TEMP_DEFROST_LOW_THRESHOLD: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
359
+ NilanProxySetpointKey.TEMP_DEFROST_HIGH_THRESHOLD: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
360
+ NilanProxySetpointKey.TEMP_HOTWATER: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
361
+ NilanProxySetpointKey.TEMP_HOTWATER_BOOST: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
362
+ NilanProxySetpointKey.TEMP_REGULATION_DEAD_BAND: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
363
+ NilanProxySetpointKey.TEMP_REHEAT_OFFSET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
364
+ NilanProxySetpointKey.TEMP_TARGET: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
365
+ NilanProxySetpointKey.TEMP_SUMMER_SUPPLY_MAX: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
366
+ NilanProxySetpointKey.TEMP_SUMMER_SUPPLY_MIN: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
367
+ NilanProxySetpointKey.TEMP_SUPPLY_MAX: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
368
+ NilanProxySetpointKey.TEMP_SUPPLY_MIN: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
369
+ NilanProxySetpointKey.TEMP_WINTER_SUPPLY_MAX: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
370
+ NilanProxySetpointKey.TEMP_WINTER_SUPPLY_MIN: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
371
+ NilanProxySetpointKey.TEMP_WINTER_MODE_THRESHOLD: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.CELSIUS, read=True),
372
+ NilanProxySetpointKey.VOC_THRESHOLD: NilanProxyPointConfig(unit_of_measurement=NilanProxyUnits.PPM, read=True),
373
+ }
374
+
375
+ class NilanProxyBaseModel:
376
+
377
+ _attr_manufacturer:str = ""
378
+ _attr_model_name:str = "Basemodel"
379
+
380
+ datapoints: Dict[NilanProxyDatapointKey, NilanProxyDatapoint] = {}
381
+ setpoints: Dict[NilanProxySetpointKey, NilanProxySetpoint] = {}
382
+ _configs: Dict[NilanProxyDatapointKey|NilanProxySetpointKey, NilanProxyPointConfig] = {}
383
+ _valueMap: Dict[NilanProxyDatapointKey|NilanProxySetpointKey, Dict[float | int, float | int | str]] = {}
384
+
385
+ def __init__(self) -> None:
386
+ return
387
+
388
+ def get_model_name(self) -> str:
389
+ return self._attr_model_name
390
+
391
+ def get_manufacturer(self) -> str:
392
+ return self._attr_manufacturer
393
+
394
+ def model_provides_datapoint(self, datapoint: NilanProxyDatapointKey) -> bool:
395
+ return datapoint in self.datapoints
396
+
397
+ def get_datapoints_for_read(self) -> List[NilanProxyDatapointKey]:
398
+ return [key for key, value in self._configs.items() if key in self.datapoints and value.get("read", False) == True]
399
+
400
+ def model_provides_setpoint(self, datapoint: NilanProxySetpointKey) -> bool:
401
+ return datapoint in self.setpoints
402
+
403
+ def get_setpoints_for_read(self) -> List[NilanProxySetpointKey]:
404
+ return [key for key, value in self._configs.items() if key in self.setpoints and value.get("read", False) == True]
405
+
406
+ def get_unit_of_measure(self, key:NilanProxyDatapointKey|NilanProxySetpointKey) -> str|None:
407
+ if key in self._configs: return self._configs[key]["unit_of_measurement"]
408
+ return NilanProxyUnits.UNDEFINED
409
+
410
+ def set_default_configs(self) -> None:
411
+ """Sets the point configurations to the standard setup, will not override already assigned records"""
412
+ # # only keep the points supported by the unit
413
+ # self._configs = {key: value for key, value in DEFAULT_CONFIGS.items() if key in self._setpoints or key in self._datapoints}
414
+
415
+ # def addMissingDefaultConfigs(self):
416
+ # Update self._configs with missing items from DEFAULT_CONFIGS
417
+ self._configs.update({
418
+ key: value for key, value in DEFAULT_CONFIGS.items()
419
+ if key not in self._configs and (key in self.setpoints or key in self.datapoints)
420
+ })
421
+
422
+ @staticmethod
423
+ def modifier_flip_bool(value:float|int) -> float|int:
424
+ """Flips the true/false state
425
+ - 1 -> 0
426
+ - 0 -> 1"""
427
+ return 1-value
428
+
429
+ @staticmethod
430
+ def modifier_seconds_to_minutes(value:float|int) -> float|int:
431
+ return round(value/60)
432
+
433
+ @staticmethod
434
+ def modifier_hours_to_days(value:float|int) -> float|int:
435
+ return round(value/24)
@@ -0,0 +1,75 @@
1
+ from .basemodel import ( NilanProxyBaseModel, NilanProxyDatapointKey, NilanProxyDatapoint, NilanProxySetpointKey, NilanProxySetpoint )
2
+
3
+
4
+ class NilanProxyCTS400(NilanProxyBaseModel):
5
+ def __init__(self, device_number:int, slave_device_number:int, slave_device_model:int):
6
+ super().__init__()
7
+
8
+ self._attr_manufacturer="Nilan"
9
+ self._attr_model_name="CTS 400"
10
+
11
+ self.datapoints = {
12
+ NilanProxyDatapointKey.BYPASS_ACTIVE: NilanProxyDatapoint(read_address=23, divider=1, signed=False),
13
+ NilanProxyDatapointKey.FAN_DUTYCYCLE_EXTRACT: NilanProxyDatapoint(read_address=24, divider=10, signed=False),
14
+ NilanProxyDatapointKey.FAN_DUTYCYCLE_SUPPLY: NilanProxyDatapoint(read_address=25, divider=10, signed=False),
15
+ NilanProxyDatapointKey.TEMP_OUTSIDE: NilanProxyDatapoint(read_address=27, divider=10, signed=True),
16
+ NilanProxyDatapointKey.TEMP_SUPPLY: NilanProxyDatapoint(read_address=28, divider=10, signed=True),
17
+ NilanProxyDatapointKey.TEMP_EXTRACT: NilanProxyDatapoint(read_address=29, divider=10, signed=True),
18
+ NilanProxyDatapointKey.TEMP_EXHAUST: NilanProxyDatapoint(read_address=30, divider=10, signed=True),
19
+ NilanProxyDatapointKey.HUMIDITY: NilanProxyDatapoint(read_address=31, divider=10, signed=False),
20
+ NilanProxyDatapointKey.HUMIDITY_AVG: NilanProxyDatapoint(read_address=46, divider=10, signed=False),
21
+ NilanProxyDatapointKey.CO2_LEVEL: NilanProxyDatapoint(read_address=47, divider=1, signed=False),
22
+ NilanProxyDatapointKey.VOC_LEVEL: NilanProxyDatapoint(read_address=48, divider=1, signed=False),
23
+ NilanProxyDatapointKey.FILTER_OK: NilanProxyDatapoint(read_address=49, divider=1, signed=False, read_modifier = self.modifier_flip_bool),
24
+ NilanProxyDatapointKey.ALARM_STATUS: NilanProxyDatapoint(read_address=50, divider=1, signed=False),
25
+ NilanProxyDatapointKey.ALARM_1_CODE: NilanProxyDatapoint(read_address=51, divider=1, signed=False),
26
+ NilanProxyDatapointKey.ALARM_2_CODE: NilanProxyDatapoint(read_address=52, divider=1, signed=False),
27
+ NilanProxyDatapointKey.ALARM_3_CODE: NilanProxyDatapoint(read_address=53, divider=1, signed=False),
28
+ NilanProxyDatapointKey.ALARM_1_INFO: NilanProxyDatapoint(read_address=56, divider=1, signed=False),
29
+ NilanProxyDatapointKey.ALARM_2_INFO: NilanProxyDatapoint(read_address=57, divider=1, signed=False),
30
+ NilanProxyDatapointKey.ALARM_3_INFO: NilanProxyDatapoint(read_address=58, divider=1, signed=False),
31
+ NilanProxyDatapointKey.FAN_LEVEL_CURRENT: NilanProxyDatapoint(read_address=63, divider=1, signed=False),
32
+ NilanProxyDatapointKey.HUMIDITY_HIGH_ACTIVE: NilanProxyDatapoint(read_address=64, divider=1, signed=False, read_modifier = self.modifier_flip_bool),
33
+ NilanProxyDatapointKey.HUMIDITY_HIGH_LEVEL: NilanProxyDatapoint(read_address=66, divider=10, signed=False),
34
+ NilanProxyDatapointKey.HUMIDITY_HIGH_LEVEL_TIME: NilanProxyDatapoint(read_address=70, divider=1, signed=False, read_modifier = self.modifier_seconds_to_minutes),
35
+ NilanProxyDatapointKey.WINTER_MODE_ACTIVE: NilanProxyDatapoint(read_address=72, divider=1, signed=False),
36
+ NilanProxyDatapointKey.FILTER_REPLACE_TIME_AGO: NilanProxyDatapoint(read_address=77, divider=1, signed=False, read_modifier = self.modifier_hours_to_days),
37
+ NilanProxyDatapointKey.DEFROST_ACTIVE: NilanProxyDatapoint(read_address=91, divider=1, signed=False),
38
+ NilanProxyDatapointKey.FILTER_REPLACE_TIME_REMAIN: NilanProxyDatapoint(read_address=110, divider=1, signed=False),
39
+ }
40
+
41
+ self.setpoints = {
42
+ NilanProxySetpointKey.ALARM_RESET: NilanProxySetpoint(read_address=30, write_address=30, divider=1, min=0, max=1, signed=False),
43
+ NilanProxySetpointKey.HUMIDITY_LOW_THRESHOLD: NilanProxySetpoint(read_address=31, write_address=31, divider=10, min=150, max=450, step=5, signed=False),
44
+ NilanProxySetpointKey.FAN_LEVEL_LOW_HUMIDITY: NilanProxySetpoint(read_address=32, write_address=32, divider=1, min=0, max=3, signed=False),
45
+ NilanProxySetpointKey.FAN_LEVEL_HIGH_HUMIDITY: NilanProxySetpoint(read_address=33, write_address=33, divider=1, min=2, max=4, signed=False),
46
+ NilanProxySetpointKey.FAN_LEVEL_HIGH_HUMIDITY_TIME: NilanProxySetpoint(read_address=34, write_address=34, divider=1, min=0, max=180, signed=False),
47
+ NilanProxySetpointKey.CO2_THRESHOLD: NilanProxySetpoint(read_address=35, write_address=35, divider=1, min=500, max=2000, signed=False),
48
+ NilanProxySetpointKey.VOC_THRESHOLD: NilanProxySetpoint(read_address=36, write_address=36, divider=1, min=500, max=2000, signed=False),
49
+ NilanProxySetpointKey.TEMP_TARGET: NilanProxySetpoint(read_address=37, write_address=37, divider=10, min=100, max=300, step=5, signed=True),
50
+ NilanProxySetpointKey.TEMP_REGULATION_DEAD_BAND: NilanProxySetpoint(read_address=38, write_address=38, divider=10, min=0, max=40, step=5, signed=True),
51
+ NilanProxySetpointKey.TEMP_DEFROST_LOW_THRESHOLD: NilanProxySetpoint(read_address=39, write_address=39, divider=10, min=10, max=50, step=5, signed=True),
52
+ NilanProxySetpointKey.TEMP_DEFROST_HIGH_THRESHOLD: NilanProxySetpoint(read_address=40, write_address=40, divider=10, min=50, max=100, step=5, signed=True),
53
+ NilanProxySetpointKey.DEFROST_MAX_TIME: NilanProxySetpoint(read_address=41, write_address=41, divider=1, min=5, max=60, signed=False),
54
+ NilanProxySetpointKey.DEFROST_BREAK_TIME: NilanProxySetpoint(read_address=43, write_address=43, divider=1, min=15, max=760, signed=False),
55
+ NilanProxySetpointKey.TEMP_WINTER_MODE_THRESHOLD: NilanProxySetpoint(read_address=45, write_address=45, divider=10, min=50, max=200, step=5, signed=True),
56
+ NilanProxySetpointKey.FILTER_REPLACE_INTERVAL: NilanProxySetpoint(read_address=50, write_address=50, divider=1, min=0, max=360, signed=False),
57
+ NilanProxySetpointKey.FILTER_REPLACE_RESET: NilanProxySetpoint(read_address=51, write_address=51, divider=1, min=0, max=1, signed=False),
58
+ NilanProxySetpointKey.TEMP_SUPPLY_MIN: NilanProxySetpoint(read_address=57, write_address=57, divider=10, min=100, max=200, step=5, signed=True),
59
+ NilanProxySetpointKey.TEMP_SUPPLY_MAX: NilanProxySetpoint(read_address=58, write_address=58, divider=10, min=100, max=500, step=5, signed=True),
60
+ NilanProxySetpointKey.FAN_LEVEL1_SUPPLY_PRESET: NilanProxySetpoint(read_address=59, write_address=59, divider=10, min=200, max=1000, signed=False),
61
+ NilanProxySetpointKey.FAN_LEVEL2_SUPPLY_PRESET: NilanProxySetpoint(read_address=60, write_address=60, divider=10, min=200, max=1000, signed=False),
62
+ NilanProxySetpointKey.FAN_LEVEL3_SUPPLY_PRESET: NilanProxySetpoint(read_address=61, write_address=61, divider=10, min=200, max=1000, signed=False),
63
+ NilanProxySetpointKey.FAN_LEVEL4_SUPPLY_PRESET: NilanProxySetpoint(read_address=62, write_address=62, divider=10, min=200, max=1000, signed=False),
64
+ NilanProxySetpointKey.FAN_LEVEL1_EXTRACT_PRESET: NilanProxySetpoint(read_address=63, write_address=63, divider=10, min=200, max=1000, signed=False),
65
+ NilanProxySetpointKey.FAN_LEVEL2_EXTRACT_PRESET: NilanProxySetpoint(read_address=64, write_address=64, divider=10, min=200, max=1000, signed=False),
66
+ NilanProxySetpointKey.FAN_LEVEL3_EXTRACT_PRESET: NilanProxySetpoint(read_address=65, write_address=65, divider=10, min=200, max=1000, signed=False),
67
+ NilanProxySetpointKey.FAN_LEVEL4_EXTRACT_PRESET: NilanProxySetpoint(read_address=66, write_address=66, divider=10, min=200, max=1000, signed=False),
68
+ NilanProxySetpointKey.FAN_LEVEL: NilanProxySetpoint(read_address=69, write_address=69, divider=1, min=1, max=4, signed=False),
69
+ NilanProxySetpointKey.ENABLE: NilanProxySetpoint(read_address=70, write_address=70, divider=1, min=0, max=1, signed=False),
70
+ NilanProxySetpointKey.FAN_LEVEL_HIGH_CO2: NilanProxySetpoint(read_address=80, write_address=80, divider=1, min=2, max=4, signed=False),
71
+ }
72
+
73
+ self.set_default_configs()
74
+
75
+ #place config modifiers here