4747
4848_LOGGER = logging .getLogger (__name__ )
4949
50- SUPPORT_FLAGS = ClimateEntityFeature .TARGET_TEMPERATURE | ClimateEntityFeature .FAN_MODE | ClimateEntityFeature .TURN_OFF | ClimateEntityFeature .TURN_ON
50+ SUPPORT_FLAGS = (
51+ ClimateEntityFeature .TARGET_TEMPERATURE
52+ | ClimateEntityFeature .FAN_MODE
53+ | ClimateEntityFeature .TURN_OFF
54+ | ClimateEntityFeature .TURN_ON
55+ )
5156
5257"""
5358Supported hvac modes:
8792)
8893
8994# constants
90- STATE_ACK = 0x00000020
91- STATE_ECO = 0x10000000
95+ STATE_ACK = 0x00000020
96+ STATE_ECO = 0x10000000
9297STATE_CLEAN = 0x04000000
93- STATE_COOL = 0x08000000
94- STATE_OFF = 0x00000020
95- STATE_ON = 0x02000000
98+ STATE_COOL = 0x08000000
99+ STATE_OFF = 0x00000020
100+ STATE_ON = 0x02000000
96101STATE_START = 0x01000000
97102
98- GET_ERRORSTATE = " \x1b RDA00067& "
99- GET_EXHFANSPEED = " \x1b REF0006D& "
100- GET_FLUGASTEMP = " \x1b RD000056& "
101- GET_PELLETSPEED = " \x1b RD40005A& "
102- GET_SETPOINT = " \x1b RC60005B& "
103- GET_STATUS = " \x1b RD90005f& "
104- GET_TEMPERATURE = " \x1b RD100057& "
105- GET_POWERLEVEL = " \x1b RD300059& "
106-
107- REMOTE_RESET = " \x1b RD60005C& "
108-
109- SET_AUGERCOR = " \x1b RD50005A&"
110- SET_EXTRACTORCOR = "\x1b RD50005B&"
111- SET_TEMPERATURE = " \x1b RF2xx0yy& "
112- SET_PELLETCOR = " \x1b RD50005B& "
113- SET_POWERLEVEL = " \x1b RF00x0yy& "
114- SET_POWEROFF = " \x1b RF000058& "
115- SET_POWERON = " \x1b RF001059& "
103+ GET_ERRORSTATE = "DA000 "
104+ GET_EXHFANSPEED = "EF000 "
105+ GET_FLUGASTEMP = "D0000 "
106+ GET_PELLETSPEED = "D4000 "
107+ GET_SETPOINT = "C6000 "
108+ GET_STATUS = "D9000 "
109+ GET_TEMPERATURE = "D1000 "
110+ GET_POWERLEVEL = "D3000 "
111+
112+ REMOTE_RESET = "D6000 "
113+
114+ SET_AUGERCOR = "D5000" #one of these is faulty
115+ SET_EXTRACTORCOR = "D5000" #only not sure which one (neither one is in use any way)
116+ SET_PELLETCOR = "D5000 "
117+ SET_POWEROFF = "F0000 "
118+ SET_POWERON = "F0010 "
119+ SET_POWERLEVEL = "F00x0 "
120+ SET_TEMPERATURE = "F2xx0 "
116121
117122# Set to True for stoves that support setpoint retrieval
118123SUPPORT_SETPOINT = False
119124
125+
120126async def async_setup_platform (
121127 hass : HomeAssistant ,
122128 config : ConfigType ,
@@ -132,9 +138,12 @@ class DuepiEvoDevice(ClimateEntity):
132138 """Representation of a DuepiEvoDevice."""
133139
134140 def __init__ (self , session , config ) -> None :
135-
136141 self ._enable_turn_on_off_backwards_compatibility = False
137- self ._attr_supported_features = ClimateEntityFeature .TARGET_TEMPERATURE | ClimateEntityFeature .TURN_OFF | ClimateEntityFeature .TURN_ON
142+ self ._attr_supported_features = (
143+ ClimateEntityFeature .TARGET_TEMPERATURE
144+ | ClimateEntityFeature .TURN_OFF
145+ | ClimateEntityFeature .TURN_ON
146+ )
138147
139148 """Initialize the DuepiEvoDevice."""
140149 self ._session = session
@@ -281,6 +290,13 @@ def fan_modes(self) -> list[str]:
281290 """Return the list of available fan modes."""
282291 return self ._fan_modes
283292
293+ def generate_command (self , command ):
294+ """Format the command string prefix with ESC R, end with a checksum and & sign."""
295+ command = "R" + command #R is part of the checksum calculation
296+ total_sum = sum (ord (char ) for char in command )
297+ lsb = total_sum & 0xFF
298+ return "\x1b " + command + format (lsb , "02X" ) + "&"
299+
284300 async def async_set_fan_mode (self , fan_mode : str ) -> None :
285301 """Set the fan mode."""
286302 if fan_mode == "" :
@@ -292,11 +308,10 @@ async def async_set_fan_mode(self, fan_mode: str) -> None:
292308 sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
293309 sock .settimeout (3.0 )
294310 sock .connect ((self ._host , self ._port ))
295- code_hex_str = hex (88 + self ._fan_mode_map [fan_mode ])
296- data_yy = SET_POWERLEVEL .replace ("yy" , code_hex_str [2 :4 ])
297311 power_level_hex_str = hex (self ._fan_mode_map [fan_mode ])
298- data_xx = data_yy .replace ("x" , power_level_hex_str [2 :3 ])
299- sock .send (data_xx .encode ())
312+ data = SET_POWERLEVEL .replace ("x" , power_level_hex_str [2 :3 ])
313+ data = self .generate_command (data )
314+ sock .send (data .encode ())
300315 data_from_server = sock .recv (10 ).decode ()
301316 current_state = int (data_from_server [1 :9 ], 16 )
302317 if not (STATE_ACK & current_state ):
@@ -327,17 +342,9 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
327342 sock .settimeout (3.0 )
328343 sock .connect ((self ._host , self ._port ))
329344 set_point_int = int (target_temperature )
330- OFFSET = (
331- 97 if set_point_int < 16 else
332- 75 if set_point_int <= 25 else
333- 82 if set_point_int <= 31 else
334- 60
335- )
336- code_hex_str = f"{ (set_point_int + OFFSET ):02X} "
337345 set_point_hex_str = f"{ set_point_int :02X} "
338- data = SET_TEMPERATURE \
339- .replace ("xx" , set_point_hex_str ) \
340- .replace ("yy" , code_hex_str )
346+ data = SET_TEMPERATURE .replace ("xx" , set_point_hex_str )
347+ data = self .generate_command (data )
341348 sock .send (data .encode ())
342349 data_from_server = sock .recv (10 ).decode ()
343350 current_state = int (data_from_server [1 :9 ], 16 )
@@ -369,7 +376,7 @@ async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
369376 sock .settimeout (3.0 )
370377 sock .connect ((self ._host , self ._port ))
371378 if hvac_mode == "off" :
372- sock .send (SET_POWEROFF .encode ())
379+ sock .send (self . generate_command ( SET_POWEROFF ) .encode ())
373380 data_from_server = sock .recv (10 ).decode ()
374381 current_state = int (data_from_server [1 :9 ], 16 )
375382 if not (STATE_ACK & current_state ):
@@ -378,7 +385,7 @@ async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
378385 )
379386 self ._hvac_mode = HVACMode .OFF
380387 elif hvac_mode == "heat" :
381- sock .send (SET_POWERON .encode ())
388+ sock .send (self . generate_command ( SET_POWERON ) .encode ())
382389 data_from_server = sock .recv (10 ).decode ()
383390 current_state = int (data_from_server [1 :9 ], 16 )
384391 if not (STATE_ACK & current_state ):
@@ -442,7 +449,7 @@ async def get_data(self, support_setpoint) -> None:
442449 sock .connect ((self ._host , self ._port ))
443450
444451 # Get Burner status
445- sock .send (GET_STATUS .encode ())
452+ sock .send (self . generate_command ( GET_STATUS ) .encode ())
446453 data_from_server = sock .recv (10 ).decode ()
447454 currentstate = int (data_from_server [1 :9 ], 16 )
448455 if STATE_START & currentstate :
@@ -461,37 +468,37 @@ async def get_data(self, support_setpoint) -> None:
461468 status = "Unknown state"
462469
463470 # Get Ambient temperature
464- sock .send (GET_TEMPERATURE .encode ())
471+ sock .send (self . generate_command ( GET_TEMPERATURE ) .encode ())
465472 data_from_server = sock .recv (10 ).decode ()
466473 if len (data_from_server ) != 0 :
467474 current_temperature = int (data_from_server [1 :5 ], 16 ) / 10.0
468475
469476 # Get Fan mode (also called fan speed or power level)
470- sock .send (GET_POWERLEVEL .encode ())
477+ sock .send (self . generate_command ( GET_POWERLEVEL ) .encode ())
471478 data_from_server = sock .recv (10 ).decode ()
472479 if len (data_from_server ) != 0 :
473480 fan_mode = int (data_from_server [1 :5 ], 16 )
474481
475482 # Get pellet speed
476- sock .send (GET_PELLETSPEED .encode ())
483+ sock .send (self . generate_command ( GET_PELLETSPEED ) .encode ())
477484 data_from_server = sock .recv (10 ).decode ()
478485 if len (data_from_server ) != 0 :
479486 pellet_speed = int (data_from_server [1 :5 ], 16 )
480487
481488 # Get FluGas temperature
482- sock .send (GET_FLUGASTEMP .encode ())
489+ sock .send (self . generate_command ( GET_FLUGASTEMP ) .encode ())
483490 data_from_server = sock .recv (10 ).decode ()
484491 if len (data_from_server ) != 0 :
485492 current_flugastemp = int (data_from_server [1 :5 ], 16 )
486493
487494 # Get Exhaust Fan speed
488- sock .send (GET_EXHFANSPEED .encode ())
495+ sock .send (self . generate_command ( GET_EXHFANSPEED ) .encode ())
489496 data_from_server = sock .recv (10 ).decode ()
490497 if len (data_from_server ) != 0 :
491498 current_exhfanspeed = int (data_from_server [1 :5 ], 16 ) * 10
492499
493500 # Get Error code
494- sock .send (GET_ERRORSTATE .encode ())
501+ sock .send (self . generate_command ( GET_ERRORSTATE ) .encode ())
495502 data_from_server = sock .recv (10 ).decode ()
496503 if len (data_from_server ) != 0 :
497504 error_code_decimal = int (data_from_server [1 :5 ], 16 )
@@ -502,7 +509,7 @@ async def get_data(self, support_setpoint) -> None:
502509 )
503510
504511 # Get & validate target temperature (Setpoint)
505- sock .send (GET_SETPOINT .encode ())
512+ sock .send (self . generate_command ( GET_SETPOINT ) .encode ())
506513 data_from_server = sock .recv (10 ).decode ()
507514 if len (data_from_server ) != 0 :
508515 target_temperature = int (data_from_server [1 :5 ], 16 )
@@ -553,7 +560,7 @@ async def remote_reset(self, error_code) -> None:
553560 sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
554561 sock .settimeout (3.0 )
555562 sock .connect ((self ._host , self ._port ))
556- sock .send (REMOTE_RESET .encode ())
563+ sock .send (self . generate_command ( REMOTE_RESET ) .encode ())
557564 data_from_server = sock .recv (10 ).decode ()
558565 data_from_server = data_from_server [1 :9 ]
559566 currentstate = int (data_from_server , 16 )
0 commit comments