From b65067010f62cff469c51e6e2e3706b07257ca11 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Fri, 15 Nov 2024 21:12:50 -0500 Subject: [PATCH 01/31] Support for HP6000ZB-xx --- custom_components/neviweb130/climate.py | 44 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 431966f..10cd40c 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -212,7 +212,10 @@ ) from .schema import ( - FANSPEED, + FAN_SPEED, + FAN_CAPABILITY, + FAN_SWING_CAPABILITY, + DISPLAY_CAPABILITY, PERIOD_VALUE, SET_SECOND_DISPLAY_SCHEMA, SET_BACKLIGHT_SCHEMA, @@ -237,6 +240,7 @@ SET_FLOOR_LIMIT_HIGH_SCHEMA, SET_ACTIVATION_SCHEMA, SET_SENSOR_TYPE_SCHEMA, + SOUND_CAPABILITY, ) _LOGGER = logging.getLogger(__name__) @@ -298,6 +302,18 @@ ATTR_TIME, ] +UPDATE_HP_ATTRIBUTES = [ + ATTR_DRSETPOINT, + ATTR_DRSTATUS, + ATTR_ROOM_SETPOINT, + ATTR_ROOM_SETPOINT_MAX, + ATTR_ROOM_SETPOINT_MIN, + ATTR_COOL_SETPOINT_MIN, + ATTR_COOL_SETPOINT_MAX, + ATTR_ROOM_TEMPERATURE, + ATTR_TEMP, +] + UPDATE_HEAT_COOL_ATTRIBUTES = [ ATTR_OUTPUT_PERCENT_DISPLAY, ATTR_ROOM_SETPOINT, @@ -330,7 +346,6 @@ ] SUPPORTED_HVAC_HP_MODES = [ - HVACMode.AUTO, HVACMode.COOL, HVACMode.DRY, HVACMode.FAN_ONLY, @@ -365,7 +380,7 @@ DEVICE_MODEL_HEAT_G2 = [300] DEVICE_MODEL_HC = [1512] DEVICE_MODEL_HEAT_PUMP = [6810, 6811, 6812] -DEVICE_MODEL_HEAT_COOL = [6500] +DEVICE_MODEL_HEAT_COOL = [6727] IMPLEMENTED_DEVICE_MODEL = DEVICE_MODEL_HEAT + DEVICE_MODEL_FLOOR + DEVICE_MODEL_LOW + DEVICE_MODEL_WIFI_FLOOR + DEVICE_MODEL_WIFI + DEVICE_MODEL_LOW_WIFI + DEVICE_MODEL_HEAT_G2 + DEVICE_MODEL_HC + DEVICE_MODEL_DOUBLE + DEVICE_MODEL_HEAT_PUMP + DEVICE_MODEL_HEAT_COOL @@ -3270,7 +3285,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._cur_temp_before = None self._target_temp = None self._operation_mode = None - self._min_temp = 5 + self._min_temp = 16 self._max_temp = 30 self._temperature_format = UnitOfTemperature.CELSIUS self._keypad = None @@ -3312,13 +3327,13 @@ def __init__(self, data, device_info, name, sku, firmware): def update(self): if self._activ: - HP_ATTRIBUTES = [ATTR_RSSI, ATTR_COOL_SETPOINT, ATTR_COOL_SETPOINT_MIN, ATTR_COOL_SETPOINT_MAX, ATTR_SYSTEM_MODE, ATTR_KEYPAD, ATTR_MODEL, ATTR_ROOM_TEMPERATURE, ATTR_ROOM_SETPOINT, ATTR_ROOM_SETPOINT_MAX, ATTR_ROOM_SETPOINT_MIN, - ATTR_TEMP, ATTR_FAN_SPEED, ATTR_FAN_SWING_VERT, ATTR_FAN_SWING_HORIZ, ATTR_FAN_CAP, ATTR_FAN_SWING_CAP, ATTR_FAN_SWING_CAP_HORIZ, ATTR_FAN_SWING_CAP_VERT, ATTR_BALANCE_PT, ATTR_HEAT_LOCK_TEMP, ATTR_COOL_LOCK_TEMP, ATTR_AVAIL_MODE, - ATTR_DISPLAY_CONF, ATTR_DISPLAY_CAP, ATTR_SOUND_CONF, ATTR_SOUND_CAP] + HP_ATTRIBUTES = [ATTR_RSSI, ATTR_COOL_SETPOINT, ATTR_SYSTEM_MODE, ATTR_KEYPAD, ATTR_MODEL, ATTR_FAN_SPEED, ATTR_FAN_SWING_VERT, ATTR_FAN_SWING_HORIZ, + ATTR_FAN_CAP, ATTR_FAN_SWING_CAP, ATTR_FAN_SWING_CAP_HORIZ, ATTR_FAN_SWING_CAP_VERT, ATTR_BALANCE_PT, ATTR_HEAT_LOCK_TEMP, ATTR_COOL_LOCK_TEMP, + ATTR_AVAIL_MODE, ATTR_DISPLAY_CONF, ATTR_DISPLAY_CAP, ATTR_SOUND_CONF, ATTR_SOUND_CAP] """Get the latest data from Neviweb and update the state.""" start = time.time() - _LOGGER.debug("Updated attributes for %s: %s", self._name, HP_ATTRIBUTES) - device_data = self._client.get_device_attributes(self._id, HP_ATTRIBUTES) + _LOGGER.debug("Updated attributes for %s: %s", self._name, UPDATE_HP_ATTRIBUTES + HP_ATTRIBUTES) + device_data = self._client.get_device_attributes(self._id, UPDATE_HP_ATTRIBUTES + HP_ATTRIBUTES) end = time.time() elapsed = round(end - start, 3) _LOGGER.debug("Updating %s (%s sec): %s", self._name, elapsed, device_data) @@ -3326,9 +3341,9 @@ def update(self): if "error" not in device_data: if "errorCode" not in device_data: self._cur_temp_before = self._cur_temp - self._cur_temp = float(device_data[ATTR_ROOM_TEMPERATURE]["value"]) if \ - device_data[ATTR_ROOM_TEMPERATURE]["value"] != None else self._cur_temp_before - self._target_temp = float(device_data[ATTR_ROOM_SETPOINT]) + self._cur_temp = float(device_data[ATTR_ROOM_TEMPERATURE]) if \ + device_data[ATTR_ROOM_TEMPERATURE] != None else self._cur_temp_before + self._target_temp = device_data[ATTR_ROOM_SETPOINT] self._min_temp = device_data[ATTR_ROOM_SETPOINT_MIN] self._max_temp = device_data[ATTR_ROOM_SETPOINT_MAX] self._target_cool = device_data[ATTR_COOL_SETPOINT] @@ -3384,10 +3399,9 @@ def update(self): def extra_state_attributes(self): """Return the state attributes.""" data = {} - data.update({'model': self._model, + data.update({'heat_pump_model': self._model, 'error_code': self._error_code, 'operation modes': self._operation_mode, - 'cool setpoint': self._target_cool, 'cool setpoint min': self._cool_min, 'cool setpoint max': self._cool_max, 'setpoint_max': self._max_temp, @@ -3426,7 +3440,7 @@ def extra_state_attributes(self): return data class Neviweb130HeatCoolThermostat(Neviweb130Thermostat): - """Implementation of Neviweb TH6500WF heat cool thermostats.""" + """Implementation of Neviweb TH6500WF, TH6250WF heat cool thermostats.""" def __init__(self, data, device_info, name, sku, firmware): """Initialize.""" From f9ebd29a37457f3eb2664d58ee5e5dc6e7aafbfb Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Fri, 15 Nov 2024 21:19:53 -0500 Subject: [PATCH 02/31] Add support for HP6000ZB-xx --- custom_components/neviweb130/schema.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/custom_components/neviweb130/schema.py b/custom_components/neviweb130/schema.py index 525c0ab..09c1847 100644 --- a/custom_components/neviweb130/schema.py +++ b/custom_components/neviweb130/schema.py @@ -97,7 +97,11 @@ TANK_HEIGHT = {23, 24, 35, 38, 47, 48, 50} LOW_FUEL_LEVEL = {0, 10, 20, 30} WATER_TEMP = {0, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55} -FANSPEED = {"high", "medium", "low", "auto", "off"} +FAN_SPEED = {"high", "medium", "low", "auto", "off"} +FAN_CAPABILITY = {"low", "med", "high", "auto"} +FAN_SWING_CAPABILITY = {"fullHorizontal", "autoHorizontal", "fullVertical", "autoVertical"} +DISPLAY_CAPABILITY = {"enable", "disable"} +SOUND_CAPABILITY = {"enable", "disable"} """Config schema.""" From 1e6b7ac1b553747e0bc9fae8da4119a2d9857ce3 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Fri, 15 Nov 2024 22:24:36 -0500 Subject: [PATCH 03/31] push version to 2.8.7 --- custom_components/neviweb130/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/neviweb130/manifest.json b/custom_components/neviweb130/manifest.json index c94d8f7..c2c3f27 100644 --- a/custom_components/neviweb130/manifest.json +++ b/custom_components/neviweb130/manifest.json @@ -5,6 +5,6 @@ "dependencies": [], "codeowners": ["@claudegel"], "requirements": [], - "version": "2.8.6", + "version": "2.8.7", "homeassistant": "2024.2.1" } From 8b29d640e117989c8177be1cf3a7a913a635e6f5 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Fri, 15 Nov 2024 22:25:04 -0500 Subject: [PATCH 04/31] push version to 2.8.7 --- custom_components.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components.json b/custom_components.json index 1498a52..4f8dfa6 100644 --- a/custom_components.json +++ b/custom_components.json @@ -1,6 +1,6 @@ { "neviweb130": { - "version": "2.8.6", + "version": "2.8.7", "local_location": "/custom_components/neviweb130/__init__.py", "remote_location": "https://github.com/claudegel/sinope-130/tree/master/custom_components/__init__.py", "visit_repo": "https://github.com/claudegel/sinope-130", From 5f2235ce71c89912c4cc7bc63bab50f8ffee3ec0 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sat, 16 Nov 2024 15:00:35 -0500 Subject: [PATCH 05/31] fix self._energy_stat_time --- custom_components/neviweb130/climate.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 10cd40c..5df60ae 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -3302,6 +3302,8 @@ def __init__(self, data, device_info, name, sku, firmware): self._fan_swing_cap_vert = None self._fan_swing_cap_horiz = None self._balance_pt = None + self._balance_pt_low = None + self._balance_pt_high = None self._heat_lock_temp = None self._cool_lock_temp = None self._avail_mode = None @@ -3321,6 +3323,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._is_wifi_floor = False self._is_floor = False self._is_low_wifi = False + self._energy_stat_time = time.time() - 1500 self._snooze = 0 self._activ = True _LOGGER.debug("Setting up %s: %s", self._name, device_info) @@ -3328,8 +3331,9 @@ def __init__(self, data, device_info, name, sku, firmware): def update(self): if self._activ: HP_ATTRIBUTES = [ATTR_RSSI, ATTR_COOL_SETPOINT, ATTR_SYSTEM_MODE, ATTR_KEYPAD, ATTR_MODEL, ATTR_FAN_SPEED, ATTR_FAN_SWING_VERT, ATTR_FAN_SWING_HORIZ, - ATTR_FAN_CAP, ATTR_FAN_SWING_CAP, ATTR_FAN_SWING_CAP_HORIZ, ATTR_FAN_SWING_CAP_VERT, ATTR_BALANCE_PT, ATTR_HEAT_LOCK_TEMP, ATTR_COOL_LOCK_TEMP, - ATTR_AVAIL_MODE, ATTR_DISPLAY_CONF, ATTR_DISPLAY_CAP, ATTR_SOUND_CONF, ATTR_SOUND_CAP] + ATTR_FAN_CAP, ATTR_FAN_SWING_CAP, ATTR_FAN_SWING_CAP_HORIZ, ATTR_FAN_SWING_CAP_VERT, ATTR_BALANCE_PT, ATTR_BALANCE_PT_TEMP_HIGH, + ATTR_BALANCE_PT_TEMP_LOW,ATTR_HEAT_LOCK_TEMP, ATTR_COOL_LOCK_TEMP, ATTR_AVAIL_MODE, ATTR_DISPLAY_CONF, ATTR_DISPLAY_CAP, + ATTR_SOUND_CONF, ATTR_SOUND_CAP] """Get the latest data from Neviweb and update the state.""" start = time.time() _LOGGER.debug("Updated attributes for %s: %s", self._name, UPDATE_HP_ATTRIBUTES + HP_ATTRIBUTES) @@ -3373,6 +3377,9 @@ def update(self): self._fan_swing_cap_vert = device_data[ATTR_FAN_SWING_CAP_VERT] self._fan_swing_cap_horiz = device_data[ATTR_FAN_SWING_CAP_HORIZ] self._balance_pt = device_data[ATTR_BALANCE_PT] + if ATTR_BALANCE_PT_TEMP_LOW in device_data: + self._balance_pt_low = device_data[ATTR_BALANCE_PT_TEMP_LOW] + self._balance_pt_high = device_data[ATTR_BALANCE_PT_TEMP_HIGH] self._heat_lock_temp = device_data[ATTR_HEAT_LOCK_TEMP] self._cool_lock_temp = device_data[ATTR_COOL_LOCK_TEMP] self._avail_mode = device_data[ATTR_AVAIL_MODE] @@ -3419,7 +3426,9 @@ def extra_state_attributes(self): 'fan_swing_capability': self._fan_swing_cap, 'fan_swing_capability_vertical': self._fan_swing_cap_vert, 'fan_swing_capability_horizontal': self._fan_swing_cap_horiz, - 'balance_point': self._balance_pt, + 'heat_pump_limit_temp': self._balance_pt, + 'min_heat_pump_limit_temp': self._balance_pt_low, + 'max_heat_pump_limit_temp': self._balance_pt_high, 'heat_lock_temp': self._heat_lock_temp, 'cool_lock_temp': self._cool_lock_temp, 'available_mode': self._avail_mode, From f5a042533d10825addc43c3d1d1ca3d21d41a5c8 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sat, 16 Nov 2024 15:28:08 -0500 Subject: [PATCH 06/31] Fix missing attributes --- custom_components/neviweb130/switch.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/custom_components/neviweb130/switch.py b/custom_components/neviweb130/switch.py index 4199655..8017aef 100644 --- a/custom_components/neviweb130/switch.py +++ b/custom_components/neviweb130/switch.py @@ -1134,7 +1134,8 @@ def update(self): if "error" not in device_data: if "errorCode" not in device_data: self._onoff = device_data[ATTR_ONOFF] - self._water_leak_status = device_data[ATTR_WATER_LEAK_STATUS] + if ATTR_WATER_LEAK_STATUS in device_data: + self._water_leak_status = device_data[ATTR_WATER_LEAK_STATUS] self._water_temp = device_data[ATTR_ROOM_TEMPERATURE] if ATTR_ERROR_CODE_SET1 in device_data and len(device_data[ATTR_ERROR_CODE_SET1]) > 0: if device_data[ATTR_ERROR_CODE_SET1]["raw"] != 0: @@ -1156,9 +1157,10 @@ def update(self): self._water_temp_min = device_data[ATTR_WATER_TEMP_MIN] self._watt_time_on = device_data[ATTR_WATT_TIME_ON] self._water_temp_time = device_data[ATTR_DR_WATER_TEMP_TIME] - self._temperature = device_data[ATTR_DR_PROTEC_STATUS]["temperature"] - self._consumption = device_data[ATTR_DR_PROTEC_STATUS]["consumption"] - self._consumption_time = device_data[ATTR_DR_PROTEC_STATUS]["consumptionOverTime"] + if ATTR_DR_PROTEC_STATUS in device_data: + self._temperature = device_data[ATTR_DR_PROTEC_STATUS]["temperature"] + self._consumption = device_data[ATTR_DR_PROTEC_STATUS]["consumption"] + self._consumption_time = device_data[ATTR_DR_PROTEC_STATUS]["consumptionOverTime"] else: _LOGGER.warning("Error in reading device %s: (%s)", self._name, device_data) else: From 6ded9961f9d7dc7da4551d08716e4ec0dabc0f7e Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sat, 16 Nov 2024 16:15:16 -0500 Subject: [PATCH 07/31] remove FAN_MODE --- custom_components/neviweb130/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 5df60ae..cb7e0b3 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -261,7 +261,7 @@ SUPPORT_HP_FLAGS = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE - | ClimateEntityFeature.FAN_MODE +# | ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON From a87048b4346cb7616661593d01edac1a57282eaa Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sat, 16 Nov 2024 16:44:05 -0500 Subject: [PATCH 08/31] comment out swing_modes --- custom_components/neviweb130/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index cb7e0b3..9460112 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -262,7 +262,7 @@ ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE # | ClimateEntityFeature.FAN_MODE - | ClimateEntityFeature.SWING_MODE +# | ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON ) @@ -270,7 +270,7 @@ SUPPORT_HC_FLAGS = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE - | ClimateEntityFeature.FAN_MODE +# | ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON ) From 7692371da7fa4d6ff63aee0bfcb78efbbb39ffe4 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sat, 16 Nov 2024 17:42:50 -0500 Subject: [PATCH 09/31] Fix heat/cool device energy stat --- custom_components/neviweb130/climate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 9460112..fd2a7fd 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -3522,6 +3522,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._is_wifi_floor = False self._is_floor = False self._is_low_wifi = False + self._energy_stat_time = time.time() - 1500 self._snooze = 0 self._activ = True _LOGGER.debug("Setting up %s: %s", self._name, device_info) From 22851ea2b6db96e7883376528a6ad5874dec5582 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sat, 16 Nov 2024 20:10:46 -0500 Subject: [PATCH 10/31] Fix missing _heat_level --- custom_components/neviweb130/climate.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index fd2a7fd..0f2d395 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -271,6 +271,7 @@ ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE # | ClimateEntityFeature.FAN_MODE +# | ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON ) @@ -1102,7 +1103,7 @@ def supported_features(self): return SUPPORT_AUX_FLAGS elif self._is_HP: return SUPPORT_HP_FLAGS - elif self._is_HC: + elif self._is_HC or self._is_hc: return SUPPORT_HC_FLAGS else: return SUPPORT_FLAGS @@ -3089,7 +3090,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._HC_device = None self._language = None self._model = None - self._fan_speed = 0 + self._fan_speed = None self._fan_swing_vert = None self._fan_swing_horiz = None self._fan_cap = None @@ -3289,12 +3290,13 @@ def __init__(self, data, device_info, name, sku, firmware): self._max_temp = 30 self._temperature_format = UnitOfTemperature.CELSIUS self._keypad = None + self._heat_level = None self._rssi = None self._target_cool = None self._cool_min = None self._cool_max = None self._model = None - self._fan_speed = 0 + self._fan_speed = None self._fan_swing_vert = None self._fan_swing_horiz = None self._fan_cap = None @@ -3468,6 +3470,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._max_temp = 30 self._temperature_format = UnitOfTemperature.CELSIUS self._time_format = "24h" + self._heat_level = 0 self._rssi = None self._keypad = None self._backlight = None @@ -3487,7 +3490,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._dual_status = None self._fan_filter_life = None self._fan_filter_remain = None - self._fan_speed = 0 + self._fan_speed = None self._balance_pt = None self._cycle = None self._aux_cycle = None From 558dea67c2abd49a8ce25f99c13f05565f33b146 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sat, 16 Nov 2024 21:10:28 -0500 Subject: [PATCH 11/31] Fix _occupancy --- custom_components/neviweb130/climate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 0f2d395..5403710 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -3291,6 +3291,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._temperature_format = UnitOfTemperature.CELSIUS self._keypad = None self._heat_level = None + self._occupancy = None self._rssi = None self._target_cool = None self._cool_min = None @@ -3492,6 +3493,7 @@ def __init__(self, data, device_info, name, sku, firmware): self._fan_filter_remain = None self._fan_speed = None self._balance_pt = None + self._occupancy = None self._cycle = None self._aux_cycle = None self._cool_cycle_length = 0 From f2ffcf28a9c8ac8fc40e60eae8f8b023dc8c11d1 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 11:46:27 -0500 Subject: [PATCH 12/31] Add many services for heat pump --- custom_components/neviweb130/climate.py | 191 +++++++++++++++++++++--- 1 file changed, 170 insertions(+), 21 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 5403710..576f7c4 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -194,12 +194,16 @@ SERVICE_SET_CLIMATE_KEYPAD_LOCK, SERVICE_SET_COOL_SETPOINT_MAX, SERVICE_SET_COOL_SETPOINT_MIN, + SERVICE_SET_COOL_LOCKOUT_TEMPERATURE, SERVICE_SET_CYCLE_OUTPUT, + SERVICE_SET_DISPLAY_CONFIG, SERVICE_SET_EARLY_START, SERVICE_SET_EM_HEAT, SERVICE_SET_FLOOR_AIR_LIMIT, SERVICE_SET_FLOOR_LIMIT_HIGH, SERVICE_SET_FLOOR_LIMIT_LOW, + SERVICE_SET_HEAT_LOCKOUT_TEMPERATURE, + SERVICE_SET_HEAT_PUMP_OPERATION_LIMIT, SERVICE_SET_HVAC_DR_OPTIONS, SERVICE_SET_HVAC_DR_SETPOINT, SERVICE_SET_PUMP_PROTECTION, @@ -207,39 +211,45 @@ SERVICE_SET_SENSOR_TYPE, SERVICE_SET_SETPOINT_MAX, SERVICE_SET_SETPOINT_MIN, + SERVICE_SET_SOUND_CONFIG, SERVICE_SET_TEMPERATURE_FORMAT, SERVICE_SET_TIME_FORMAT, ) from .schema import ( + DISPLAY_CAPABILITY, FAN_SPEED, FAN_CAPABILITY, FAN_SWING_CAPABILITY, - DISPLAY_CAPABILITY, PERIOD_VALUE, - SET_SECOND_DISPLAY_SCHEMA, + SET_ACTIVATION_SCHEMA, + SET_AIR_FLOOR_MODE_SCHEMA, + SET_AUX_CYCLE_OUTPUT_SCHEMA, + SET_AUXILIARY_LOAD_SCHEMA, SET_BACKLIGHT_SCHEMA, SET_CLIMATE_KEYPAD_LOCK_SCHEMA, + SET_COOL_LOCKOUT_TEMPERATURE_SCHEMA, + SET_COOL_SETPOINT_MAX_SCHEMA, + SET_COOL_SETPOINT_MIN_SCHEMA, + SET_CYCLE_OUTPUT_SCHEMA, + SET_DISPLAY_CONFIG_SCHEMA, + SET_EARLY_START_SCHEMA, SET_EM_HEAT_SCHEMA, - SET_TIME_FORMAT_SCHEMA, - SET_TEMPERATURE_FORMAT_SCHEMA, - SET_SETPOINT_MAX_SCHEMA, - SET_SETPOINT_MIN_SCHEMA, SET_FLOOR_AIR_LIMIT_SCHEMA, - SET_EARLY_START_SCHEMA, - SET_AIR_FLOOR_MODE_SCHEMA, + SET_FLOOR_LIMIT_HIGH_SCHEMA, + SET_FLOOR_LIMIT_LOW_SCHEMA, + SET_HEAT_LOCKOUT_TEMPERATURE_SCHEMA, + SET_HEAT_PUMP_OPERATION_LIMIT_SCHEMA, SET_HVAC_DR_OPTIONS_SCHEMA, SET_HVAC_DR_SETPOINT_SCHEMA, - SET_COOL_SETPOINT_MAX_SCHEMA, - SET_COOL_SETPOINT_MIN_SCHEMA, - SET_AUXILIARY_LOAD_SCHEMA, - SET_AUX_CYCLE_OUTPUT_SCHEMA, - SET_CYCLE_OUTPUT_SCHEMA, SET_PUMP_PROTECTION_SCHEMA, - SET_FLOOR_LIMIT_LOW_SCHEMA, - SET_FLOOR_LIMIT_HIGH_SCHEMA, - SET_ACTIVATION_SCHEMA, + SET_SECOND_DISPLAY_SCHEMA, SET_SENSOR_TYPE_SCHEMA, + SET_SETPOINT_MAX_SCHEMA, + SET_SETPOINT_MIN_SCHEMA, + SET_SOUND_CONFIG_SCHEMA, + SET_TEMPERATURE_FORMAT_SCHEMA, + SET_TIME_FORMAT_SCHEMA, SOUND_CAPABILITY, ) @@ -371,6 +381,11 @@ PRESET_NONE, ] +PRESET_HC_MODES = [ + PRESET_AWAY, + PRESET_NONE, +] + DEVICE_MODEL_LOW = [7372] DEVICE_MODEL_LOW_WIFI = [739] DEVICE_MODEL_FLOOR = [737] @@ -679,11 +694,11 @@ def set_activation_service(service): """Activate or deactivate Neviweb polling for missing device.""" entity_id = service.data[ATTR_ENTITY_ID] value = {} - for switch in entities: - if switch.entity_id == entity_id: - value = {"id": switch.unique_id, "active": service.data[ATTR_ACTIVE]} - switch.set_activation(value) - switch.schedule_update_ha_state(True) + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "active": service.data[ATTR_ACTIVE]} + thermostat.set_activation(value) + thermostat.schedule_update_ha_state(True) break def set_sensor_type_service(service): @@ -710,6 +725,61 @@ def set_em_heat_service(service): thermostat.schedule_update_ha_state(True) break + def set_heat_pump_operation_limit_service(service): + """Set minimum temperature for heat pump device operation.""" + entity_id = service.data[ATTR_ENTITY_ID] + value = {} + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "temp": service.data[ATTR_BALANCE_PT]} + thermostat.set_heat_pump_operation_limit(value) + thermostat.schedule_update_ha_state(True) + break + + def set_heat_lockout_temperature_service(service): + """Set maximum outside temperature limit to allow heating device operation.""" + entity_id = service.data[ATTR_ENTITY_ID] + value = {} + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "temp": service.data[ATTR_HEAT_LOCK_TEMP]} + thermostat.set_heat_lockout_temperature(value) + thermostat.schedule_update_ha_state(True) + break + + def set_cool_lockout_temperature_service(service): + """Set minimum outside temperature limit to allow cooling device operation.""" + entity_id = service.data[ATTR_ENTITY_ID] + value = {} + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "temp": service.data[ATTR_COOL_LOCK_TEMP]} + thermostat.set_cool_lockout_temperature(value) + thermostat.schedule_update_ha_state(True) + break + + def set_display_config_service(service): + """Set display on/off for heat pump.""" + entity_id = service.data[ATTR_ENTITY_ID] + value = {} + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "display": service.data[ATTR_DISPLAY_CONF]} + thermostat.set_display_config(value) + thermostat.schedule_update_ha_state(True) + break + + def set_sound_config_service(service): + """Set sound on/off for heat pump.""" + entity_id = service.data[ATTR_ENTITY_ID] + value = {} + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "sound": service.data[ATTR_SOUND_CONF]} + thermostat.set_sound_config(value) + thermostat.schedule_update_ha_state(True) + break + hass.services.async_register( DOMAIN, SERVICE_SET_SECOND_DISPLAY, @@ -871,6 +941,41 @@ def set_em_heat_service(service): schema=SET_EM_HEAT_SCHEMA, ) + hass.services.async_register( + DOMAIN, + SERVICE_SET_HEAT_PUMP_OPERATION_LIMIT, + set_heat_pump_operation_limit_service, + schema=SET_HEAT_PUMP_OPERATION_LIMIT_SCHEMA, + ) + + hass.services.async_register( + DOMAIN, + SERVICE_SET_COOL_LOCKOUT_TEMPERATURE, + set_cool_lockout_temperature_service, + schema=SET_COOL_LOCKOUT_TEMPERATURE_SCHEMA, + ) + + hass.services.async_register( + DOMAIN, + SERVICE_SET_HEAT_LOCKOUT_TEMPERATURE, + set_heat_lockout_temperature_service, + schema=SET_HEAT_LOCKOUT_TEMPERATURE_SCHEMA, + ) + + hass.services.async_register( + DOMAIN, + SERVICE_SET_DISPLAY_CONFIG, + set_display_config_service, + schema=SET_DISPLAY_CONFIG_SCHEMA, + ) + + hass.services.async_register( + DOMAIN, + SERVICE_SET_SOUND_CONFIG, + set_sound_config_service, + schema=SET_SOUND_CONFIG_SCHEMA, + ) + def neviweb_to_ha(value): keys = [k for k, v in HA_TO_NEVIWEB_PERIOD.items() if v == value] if keys: @@ -1211,6 +1316,8 @@ def preset_modes(self): return PRESET_WIFI_MODES elif self._is_HP: return PRESET_HP_MODES + elif self._is_HC or self._is_hc: + return PRESET_HC_MODES else: return PRESET_MODES @@ -1575,6 +1682,48 @@ def set_activation(self, value): action = value["active"] self._activ = action + def set_heat_pump_operation_limit(self, value): + """Set minimum temperature for heat pump operation.""" + temp = value["temp"] + entity = value["id"] + if temp < self._balance_pt_low: + temp = self._balance_pt_low + self._client.set_heat_pump_limit( + entity, temp) + self._balance_pt = temp + + def set_heat_lockout_temperature(self, value): + """Set maximum outside temperature limit to allow heat device operation.""" + temp = value["temp"] + entity = value["id"] + self._client.set_heat_lockout( + entity, temp) + self._heat_lockout_temp = temp + + def set_cool_lockout_temperature(self, value): + """Set minimum outside temperature limit to allow cooling device operation.""" + temp = value["temp"] + entity = value["id"] + self._client.set_cool_lockout( + entity, temp) + self._cool_lock_temp = temp + + def set_display_config(self, value): + """Set display on/off for heat pump.""" + display = value["display"] + entity = value["id"] + self._client.set_hp_display( + entity, display) + self._display_conf = display + + def set_sound_config(self, value): + """Set sound on/off for heat pump.""" + sound = value["sound"] + entity = value["id"] + self._client.set_hp_sound( + entity, sound) + self._sound_conf = sound + def do_stat(self, start): """Get device energy statistic.""" if start - self._energy_stat_time > STAT_INTERVAL and self._energy_stat_time != 0: From fc5c5c72335fd5a11623dc97ba31713bd36e49be Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 11:54:01 -0500 Subject: [PATCH 13/31] Add many services for heat pump --- custom_components/neviweb130/__init__.py | 165 ++++++++++++++--------- 1 file changed, 100 insertions(+), 65 deletions(-) diff --git a/custom_components/neviweb130/__init__.py b/custom_components/neviweb130/__init__.py index 71ea3c9..d4b638d 100644 --- a/custom_components/neviweb130/__init__.py +++ b/custom_components/neviweb130/__init__.py @@ -25,91 +25,96 @@ PRESET_HOME, ) from .const import ( - DOMAIN, - CONF_HOMEKIT_MODE, - CONF_NETWORK, - CONF_NETWORK2, - CONF_NOTIFY, - CONF_STAT_INTERVAL, - ATTR_INTENSITY, - ATTR_ONOFF, - ATTR_ONOFF2, - ATTR_POWER_MODE, - ATTR_POWER_SUPPLY, - ATTR_SETPOINT_MODE, - ATTR_ROOM_SETPOINT, - ATTR_ROOM_SETPOINT_MIN, - ATTR_ROOM_SETPOINT_MAX, - ATTR_KEYPAD, - ATTR_KEY_DOUBLE_UP, + ATTR_AUX_CYCLE, ATTR_BACKLIGHT, ATTR_BACKLIGHT_AUTO_DIM, - ATTR_DISPLAY2, - ATTR_TIMER, - ATTR_TIMER2, - ATTR_TIME, - ATTR_TEMP, - ATTR_LED_ON_INTENSITY, - ATTR_LED_OFF_INTENSITY, - ATTR_LED_ON_COLOR, - ATTR_LED_OFF_COLOR, - ATTR_LIGHT_WATTAGE, - ATTR_LEAK_ALERT, + ATTR_BALANCE_PT, ATTR_BATT_ALERT, ATTR_BATTERY_TYPE, - ATTR_TEMP_ALERT, + ATTR_COLD_LOAD_PICKUP_REMAIN_TIME, ATTR_CONF_CLOSURE, - ATTR_MOTOR_TARGET, - ATTR_FLOOR_AIR_LIMIT, - ATTR_SIGNATURE, - ATTR_EARLY_START, - ATTR_FLOOR_MODE, - ATTR_FLOOR_SENSOR, - ATTR_MODE, - ATTR_PHASE_CONTROL, - ATTR_OCCUPANCY, - ATTR_SYSTEM_MODE, + ATTR_CONTROLLED_DEVICE, + ATTR_COOL_LOCK_TEMP, + ATTR_COOL_SETPOINT_MAX, + ATTR_COOL_SETPOINT_MIN, + ATTR_CYCLE, + ATTR_CYCLE_OUTPUT2, + ATTR_DISPLAY_CONF, + ATTR_DISPLAY2, ATTR_DRSETPOINT, ATTR_DRSTATUS, + ATTR_EARLY_START, ATTR_FLOOR_AUX, - ATTR_FLOOR_OUTPUT2, + ATTR_FLOOR_AIR_LIMIT, ATTR_FLOOR_MAX, ATTR_FLOOR_MIN, - ATTR_CYCLE_OUTPUT2, - ATTR_AUX_CYCLE, - ATTR_CYCLE, - ATTR_PUMP_PROTEC, - ATTR_PUMP_PROTEC_DURATION, - ATTR_PUMP_PROTEC_PERIOD, - ATTR_TANK_SIZE, - ATTR_CONTROLLED_DEVICE, - ATTR_COOL_SETPOINT_MIN, - ATTR_COOL_SETPOINT_MAX, - ATTR_WATER_TEMP_MIN, - ATTR_FLOW_METER_CONFIG, ATTR_FLOW_ENABLED, - ATTR_FLOW_ALARM1_PERIOD, - ATTR_FLOW_ALARM1_OPTION, + ATTR_FLOW_METER_CONFIG, + ATTR_FLOOR_MODE, + ATTR_FLOOR_OUTPUT2, + ATTR_FLOOR_SENSOR, ATTR_FLOW_ALARM1_LENGHT, + ATTR_FLOW_ALARM1_OPTION, + ATTR_FLOW_ALARM1_PERIOD, ATTR_FLOW_THRESHOLD, - ATTR_WIFI_KEYPAD, - ATTR_TANK_TYPE, - ATTR_GAUGE_TYPE, - ATTR_FUEL_PERCENT_ALERT, - ATTR_TANK_HEIGHT, ATTR_FUEL_ALERT, + ATTR_FUEL_PERCENT_ALERT, + ATTR_GAUGE_TYPE, + ATTR_HEAT_LOCK_TEMP, + ATTR_INPUT_1_OFF_DELAY, + ATTR_INPUT_2_OFF_DELAY, + ATTR_INPUT_1_ON_DELAY, + ATTR_INPUT_2_ON_DELAY, + ATTR_INTENSITY, + ATTR_KEY_DOUBLE_UP, + ATTR_KEYPAD, + ATTR_LEAK_ALERT, + ATTR_LED_OFF_COLOR, + ATTR_LED_OFF_INTENSITY, + ATTR_LED_ON_COLOR, + ATTR_LED_ON_INTENSITY, + ATTR_LIGHT_WATTAGE, + ATTR_MODE, + ATTR_MOTOR_TARGET, ATTR_NAME_1, ATTR_NAME_2, + ATTR_OCCUPANCY, + ATTR_ONOFF, + ATTR_ONOFF2, ATTR_OUTPUT_NAME_1, ATTR_OUTPUT_NAME_2, - ATTR_COLD_LOAD_PICKUP_REMAIN_TIME, - ATTR_INPUT_1_ON_DELAY, - ATTR_INPUT_2_ON_DELAY, - ATTR_INPUT_1_OFF_DELAY, - ATTR_INPUT_2_OFF_DELAY, + ATTR_PHASE_CONTROL, + ATTR_POWER_MODE, + ATTR_POWER_SUPPLY, + ATTR_PUMP_PROTEC, + ATTR_PUMP_PROTEC_DURATION, + ATTR_PUMP_PROTEC_PERIOD, + ATTR_ROOM_SETPOINT, + ATTR_ROOM_SETPOINT_MIN, + ATTR_ROOM_SETPOINT_MAX, + ATTR_SETPOINT_MODE, + ATTR_SIGNATURE, + ATTR_SOUND_CONF, + ATTR_SYSTEM_MODE, + ATTR_TANK_HEIGHT, + ATTR_TANK_SIZE, + ATTR_TANK_TYPE, + ATTR_TIMER, + ATTR_TIMER2, + ATTR_TIME, + ATTR_TEMP, + ATTR_TEMP_ALERT, + ATTR_WATER_TEMP_MIN, + ATTR_WIFI_KEYPAD, + DOMAIN, + CONF_HOMEKIT_MODE, + CONF_NETWORK, + CONF_NETWORK2, + CONF_NOTIFY, + CONF_STAT_INTERVAL, MODE_AWAY, MODE_HOME, - MODE_MANUAL + MODE_MANUAL, ) from .schema import ( @@ -955,6 +960,36 @@ def set_input_output_names(self, device_id, in1, in2, out1, out2): _LOGGER.debug("in/out names.data = %s", data) self.set_device_attributes(device_id, data) + def set_heat_pump_limit(self, device_id, temp): + """Set minimum temperature for heat pump operation.""" + data = {ATTR_BALANCE_PT: temp} + _LOGGER.debug("Heat pump limit value.data = %s", data) + self.set_device_attributes(device_id, data) + + def set_heat_lockout(self, device_id, temp): + """Set maximum outside temperature limit to allow heating device operation.""" + data = {ATTR_HEAT_LOCK_TEMP: temp} + _LOGGER.debug("Heat lockout limit value.data = %s", data) + self.set_device_attributes(device_id, data) + + def set_cool_lockout(self, device_id, temp): + """Set minimum outside temperature limit to allow cooling devices operation.""" + data = {ATTR_COOL_LOCK_TEMP: temp} + _LOGGER.debug("Cool lockout limit value.data = %s", data) + self.set_device_attributes(device_id, data) + + def set_hp_display(self, device_id, display): + """Set display on/off for heat pump.""" + data = {ATTR_DISPLAY_CONF: display} + _LOGGER.debug("Display config value.data = %s", data) + self.set_device_attributes(device_id, data) + + def set_hp_sound(self, device_id, sound): + """Set display on/off for heat pump.""" + data = {ATTR_SOUND_CONF: sound} + _LOGGER.debug("Sound config value.data = %s", data) + self.set_device_attributes(device_id, data) + def set_device_attributes(self, device_id, data): """Ser devices attributes.""" result = 1 From fdfb8f37745ea0bd512bbdaf613440c4d5880925 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 11:56:30 -0500 Subject: [PATCH 14/31] Add many services for heat pump --- custom_components/neviweb130/const.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/custom_components/neviweb130/const.py b/custom_components/neviweb130/const.py index 3f20d4e..e85298d 100644 --- a/custom_components/neviweb130/const.py +++ b/custom_components/neviweb130/const.py @@ -276,3 +276,8 @@ SERVICE_SET_REMAINING_TIME = "set_remaining_time" SERVICE_SET_ON_OFF_INPUT_DELAY = "set_on_off_input_delay" SERVICE_SET_EM_HEAT = "set_em_heat" +SERVICE_SET_HEAT_PUMP_OPERATION_LIMIT = "set_heat_pump_operation_limit" +SERVICE_SET_COOL_LOCKOUT_TEMPERATURE = "set_cool_lockout_temperature" +SERVICE_SET_HEAT_LOCKOUT_TEMPERATURE = "set_heat_lockout_temperature" +SERVICE_SET_DISPLAY_CONFIG = "set_display_config" +SERVICE_SET_SOUND_CONFIG = "set_sound_config" From 8d3a967847e03fd501db9c4c5fad2ca1b1f65d34 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 12:02:12 -0500 Subject: [PATCH 15/31] Add many services schema for heat pump --- custom_components/neviweb130/schema.py | 64 +++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/custom_components/neviweb130/schema.py b/custom_components/neviweb130/schema.py index 09c1847..05d4551 100644 --- a/custom_components/neviweb130/schema.py +++ b/custom_components/neviweb130/schema.py @@ -15,24 +15,21 @@ ) from .const import ( - DOMAIN, - CONF_HOMEKIT_MODE, - CONF_NETWORK, - CONF_NETWORK2, - CONF_NOTIFY, - CONF_STAT_INTERVAL, ATTR_ACTIVE, ATTR_BACKLIGHT, + ATTR_BALANCE_PT, ATTR_BATT_ALERT, ATTR_BATTERY_TYPE, ATTR_BLUE, ATTR_CLOSE_VALVE, ATTR_COLD_LOAD_PICKUP_REMAIN_TIME, ATTR_CONF_CLOSURE, + ATTR_COOL_LOCK_TEMP, ATTR_COOL_SETPOINT_MAX, ATTR_COOL_SETPOINT_MIN, ATTR_DELAY, ATTR_DISPLAY2, + ATTR_DISPLAY_CONF, ATTR_DRACTIVE, ATTR_EARLY_START, ATTR_FLOOR_AIR_LIMIT, @@ -46,6 +43,7 @@ ATTR_FUEL_PERCENT_ALERT, ATTR_GAUGE_TYPE, ATTR_GREEN, + ATTR_HEAT_LOCK_TEMP, ATTR_INPUT_NUMBER, ATTR_INTENSITY, ATTR_KEY_DOUBLE_UP, @@ -66,6 +64,7 @@ ATTR_ROOM_SETPOINT_MAX, ATTR_ROOM_SETPOINT_MIN, ATTR_SETPOINT, + ATTR_SOUND_CONF, ATTR_STATE, ATTR_STATUS, ATTR_TANK_HEIGHT, @@ -79,6 +78,12 @@ ATTR_TYPE, ATTR_VALUE, ATTR_WATER_TEMP_MIN, + DOMAIN, + CONF_HOMEKIT_MODE, + CONF_NETWORK, + CONF_NETWORK2, + CONF_NOTIFY, + CONF_STAT_INTERVAL, ) """Default parameters values.""" @@ -317,6 +322,51 @@ } ) +SET_HEAT_PUMP_OPERATION_LIMIT_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_BALANCE_PT): vol.All( + vol.Coerce(int), vol.Range(min=-30, max=-5) + ), + } +) + +SET_COOL_LOCKOUT_TEMPERATURE_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_COOL_LOCK_TEMP): vol.All( + vol.Coerce(int), vol.Range(min=10, max=30) + ), + } +) + +SET_HEAT_LOCKOUT_TEMPERATURE_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_HEAT_LOCK_TEMP): vol.All( + vol.Coerce(int), vol.Range(min=10, max=30) + ), + } +) + +SET_DISPLAY_CONFIG_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_DISPLAY_CONF): vol.All( + cv.ensure_list, [vol.In(DISPLAY_CAPABILITY)] + ), + } +) + +SET_SOUND_CONFIG_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_SOUND_CONF): vol.All( + cv.ensure_list, [vol.In(SOUND_CAPABILITY)] + ), + } +) + """light schema.""" SET_LIGHT_KEYPAD_LOCK_SCHEMA = vol.Schema( @@ -378,7 +428,7 @@ vol.Required(ATTR_KEY_DOUBLE_UP): vol.In(["On", "Off"]), } ) - + """"Switch schema.""" SET_SWITCH_KEYPAD_LOCK_SCHEMA = vol.Schema( From 255ae0ed8546fa7d63b01e7f4b7e9b9c3ae2273d Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 12:05:20 -0500 Subject: [PATCH 16/31] Add many services description for heat pump --- custom_components/neviweb130/services.yaml | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/custom_components/neviweb130/services.yaml b/custom_components/neviweb130/services.yaml index c49727f..e1d5ec0 100644 --- a/custom_components/neviweb130/services.yaml +++ b/custom_components/neviweb130/services.yaml @@ -602,3 +602,53 @@ set_em_heat: example: "climate.neviweb130_climate_office_floor" value: description: Set to «on» or «off» to turn_on or turn_off emergency heat. + +set_heat_pump_operation_limit: + description: Set the outside low temperature limit for heat pump operation. Depend on heat pump model. + fields: + entity_id: + description: Name(s) of neviweb130 thermostat HP6000 device. + example: "climate.neviweb130_climate_HP6000ZB-GE" + balancePoint: + description: possible values, from -5 to -30 + example: -25 + +set_cool_lockout_temperature: + description: Do not allow cooling for heat pump, if outside temperature is below this temperature. + fields: + entity_id: + description: Name(s) of neviweb130 thermostat HP6000 device. + example: "climate.neviweb130_climate_HP6000ZB-GE" + coolLockoutTemperature: + description: possible values, from 10 to 30 oC + example: 20 + +set_heat_lockout_temperature: + description: Do not allow heating for registered thermostats, if outside temperature is above this temperature. + fields: + entity_id: + description: Name(s) of neviweb130 thermostat HP6000 device. + example: "climate.neviweb130_climate_HP6000ZB-GE" + heatLockoutTemperature: + description: possible values, from 10 to 30 oC + example: 22 + +set_display_config: + description: Set heat pump display on/off. + fields: + entity_id: + description: Name(s) of neviweb130 thermostat HP6000 device. + example: "climate.neviweb130_climate_HP6000ZB-GE" + displayConfig: + description: possible values, enabled, disabled. + example: "disabled" + +set_sound_config: + description: Set heat pump sound on/off. + fields: + entity_id: + description: Name(s) of neviweb130 thermostat HP6000 device. + example: "climate.neviweb130_climate_HP6000ZB-GE" + soundConfig: + description: possible values, enabled, disabled. + example: "disabled" From 8425a3f42d7cbb1c4a3f5aa146465ef74e222086 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 14:26:29 -0500 Subject: [PATCH 17/31] Extract activ capability for each HP device model --- custom_components/neviweb130/climate.py | 37 +++++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 576f7c4..2190087 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -339,6 +339,8 @@ ATTR_TIME, ] +FULL_SWING = ['swingFullRange', 'off'] + SUPPORTED_HVAC_WIFI_MODES = [ HVACMode.AUTO, HVACMode.HEAT, @@ -364,6 +366,19 @@ HVACMode.OFF, ] +SUPPORTED_HVAC_HEAT_MODES = [ + HVACMode.FAN_ONLY, + HVACMode.HEAT, + HVACMode.OFF, +] + +SUPPORTED_HVAC_COOL_MODES = [ + HVACMode.COOL, + HVACMode.DRY, + HVACMode.FAN_ONLY, + HVACMode.OFF, +] + PRESET_WIFI_MODES = [ PRESET_AWAY, PRESET_HOME, @@ -1004,6 +1019,11 @@ def lock_to_ha(lock): case "partialLock": return "Tamper protection" +def extract_capability(cap): + """Extract capability which are True for each HP device.""" + value = {i for i in cap if cap[i]==True} + return FULL_SWING + sorted(value) + class Neviweb130Thermostat(ClimateEntity): """Implementation of Neviweb TH1123ZB, TH1124ZB thermostat.""" @@ -1274,7 +1294,12 @@ def hvac_modes(self): elif self._is_hc or self._is_HC: return SUPPORTED_HVAC_HC_MODES elif self._is_HP: - return SUPPORTED_HVAC_HP_MODES + if self._avail_mode = "heatingOnly": + return SUPPORTED_HVAC_HEAT_MODES + elif self._avail_mode = "coolingOnly": + return SUPPORTED_HVAC_COOL_MODES + else: + return SUPPORTED_HVAC_HP_MODES else: return SUPPORTED_HVAC_MODES @@ -3569,15 +3594,15 @@ def extra_state_attributes(self): 'keypad': lock_to_ha(self._keypad), 'fan_speed': self._fan_speed, 'display_conf': self._display_conf, - 'display_capability': self._display_cap, + 'display_capability': extract_capability(self._display_cap), 'sound_conf': self._sound_conf, - 'sound_capability': self._sound_cap, + 'sound_capability': extract_capability(self._sound_cap), 'fan_swing_vertical': self._fan_swing_vert, 'fan_swing_horizontal': self._fan_swing_horiz, 'fan_capability': self._fan_cap, - 'fan_swing_capability': self._fan_swing_cap, - 'fan_swing_capability_vertical': self._fan_swing_cap_vert, - 'fan_swing_capability_horizontal': self._fan_swing_cap_horiz, + 'fan_swing_capability': extract_capability(self._fan_swing_cap), + 'fan_swing_capability_vertical': extract_capability(self._fan_swing_cap_vert), + 'fan_swing_capability_horizontal': extract_capability(self._fan_swing_cap_horiz), 'heat_pump_limit_temp': self._balance_pt, 'min_heat_pump_limit_temp': self._balance_pt_low, 'max_heat_pump_limit_temp': self._balance_pt_high, From 226b4efc88d7ad7d037dd3f8d93e49e36b5d4f00 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 14:51:34 -0500 Subject: [PATCH 18/31] Typo fix --- custom_components/neviweb130/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 2190087..d65f3de 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -1294,9 +1294,9 @@ def hvac_modes(self): elif self._is_hc or self._is_HC: return SUPPORTED_HVAC_HC_MODES elif self._is_HP: - if self._avail_mode = "heatingOnly": + if self._avail_mode == "heatingOnly": return SUPPORTED_HVAC_HEAT_MODES - elif self._avail_mode = "coolingOnly": + elif self._avail_mode == "coolingOnly": return SUPPORTED_HVAC_COOL_MODES else: return SUPPORTED_HVAC_HP_MODES From 7af64a1b293aab92b03dbd83d32e886f543fb0d5 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 19:41:27 -0500 Subject: [PATCH 19/31] Fix capability extraction --- custom_components/neviweb130/climate.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index d65f3de..4c7aabc 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -1019,10 +1019,15 @@ def lock_to_ha(lock): case "partialLock": return "Tamper protection" +def extract_capability_full(cap): + """Extract swing capability which are True for each HP device.""" + value = {i for i in cap if cap[i]==True} + return FULL_SWING + sorted(value) + def extract_capability(cap): """Extract capability which are True for each HP device.""" value = {i for i in cap if cap[i]==True} - return FULL_SWING + sorted(value) + return sorted(value) class Neviweb130Thermostat(ClimateEntity): """Implementation of Neviweb TH1123ZB, TH1124ZB thermostat.""" @@ -3601,8 +3606,8 @@ def extra_state_attributes(self): 'fan_swing_horizontal': self._fan_swing_horiz, 'fan_capability': self._fan_cap, 'fan_swing_capability': extract_capability(self._fan_swing_cap), - 'fan_swing_capability_vertical': extract_capability(self._fan_swing_cap_vert), - 'fan_swing_capability_horizontal': extract_capability(self._fan_swing_cap_horiz), + 'fan_swing_capability_vertical': extract_capability_full(self._fan_swing_cap_vert), + 'fan_swing_capability_horizontal': extract_capability_full(self._fan_swing_cap_horiz), 'heat_pump_limit_temp': self._balance_pt, 'min_heat_pump_limit_temp': self._balance_pt_low, 'max_heat_pump_limit_temp': self._balance_pt_high, From 3e10dbbff3dbda76b132742279a954e9ad057987 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 21:59:57 -0500 Subject: [PATCH 20/31] Add fan set_swing.... services --- custom_components/neviweb130/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/custom_components/neviweb130/__init__.py b/custom_components/neviweb130/__init__.py index d4b638d..6d76c2d 100644 --- a/custom_components/neviweb130/__init__.py +++ b/custom_components/neviweb130/__init__.py @@ -44,6 +44,8 @@ ATTR_DRSETPOINT, ATTR_DRSTATUS, ATTR_EARLY_START, + ATTR_FAN_SWING_HORIZ, + ATTR_FAN_SWING_VERT, ATTR_FLOOR_AUX, ATTR_FLOOR_AIR_LIMIT, ATTR_FLOOR_MAX, @@ -990,6 +992,18 @@ def set_hp_sound(self, device_id, sound): _LOGGER.debug("Sound config value.data = %s", data) self.set_device_attributes(device_id, data) + def set_swing_horizontal(self, device_id, swing): + """Set horizontal fan swing action for heat pump.""" + data = {ATTR_FAN_SWING_HORIZ: swing} + _LOGGER.debug("Fan horizontal swing value.data = %s", data) + self.set_device_attributes(device_id, data) + + def set_swing_vertical(self, device_id, swing): + """Set vertical fan swing action for heat pump.""" + data = {ATTR_FAN_SWING_VERT: swing} + _LOGGER.debug("Fan vertical swing value.data = %s", data) + self.set_device_attributes(device_id, data) + def set_device_attributes(self, device_id, data): """Ser devices attributes.""" result = 1 From def74603c876560b8796ff81a78924c08f38661a Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 22:06:30 -0500 Subject: [PATCH 21/31] Add fan set_swing... services --- custom_components/neviweb130/climate.py | 66 +++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 4c7aabc..14d6176 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -199,6 +199,8 @@ SERVICE_SET_DISPLAY_CONFIG, SERVICE_SET_EARLY_START, SERVICE_SET_EM_HEAT, + SERVICE_SET_FAN_SWING_HORIZONTAL, + SERVICE_SET_FAN_SWING_VERTICAL, SERVICE_SET_FLOOR_AIR_LIMIT, SERVICE_SET_FLOOR_LIMIT_HIGH, SERVICE_SET_FLOOR_LIMIT_LOW, @@ -235,6 +237,8 @@ SET_DISPLAY_CONFIG_SCHEMA, SET_EARLY_START_SCHEMA, SET_EM_HEAT_SCHEMA, + SET_FAN_SWING_HORIZONTALL_SCHEMA, + SET_FAN_SWING_VERTICAL_SCHEMA, SET_FLOOR_AIR_LIMIT_SCHEMA, SET_FLOOR_LIMIT_HIGH_SCHEMA, SET_FLOOR_LIMIT_LOW_SCHEMA, @@ -795,6 +799,28 @@ def set_sound_config_service(service): thermostat.schedule_update_ha_state(True) break + def set_fan_swing_horizontal_service(service): + """Set horizontal fan swing action for heat pump.""" + entity_id = service.data[ATTR_ENTITY_ID] + value = {} + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "swing": service.data[ATTR_FAN_SWING_HORIZ]} + thermostat.set_fan_swing_horizontal(value) + thermostat.schedule_update_ha_state(True) + break + + def set_fan_swing_vertical_service(service): + """Set vertical fan swing action for heat pump.""" + entity_id = service.data[ATTR_ENTITY_ID] + value = {} + for thermostat in entities: + if thermostat.entity_id == entity_id: + value = {"id": thermostat.unique_id, "swing": service.data[ATTR_FAN_SWING_VERT]} + thermostat.set_fan_swing_vertical(value) + thermostat.schedule_update_ha_state(True) + break + hass.services.async_register( DOMAIN, SERVICE_SET_SECOND_DISPLAY, @@ -991,6 +1017,20 @@ def set_sound_config_service(service): schema=SET_SOUND_CONFIG_SCHEMA, ) + hass.services.async_register( + DOMAIN, + SERVICE_SET_FAN_SWING_HORIZONTAL, + set_fan_swing_horizontal_service, + schema=SET_FAN_SWING_HORIZONTALL_SCHEMA, + ) + + hass.services.async_register( + DOMAIN, + SERVICE_SET_FAN_SWING_VERTICAL, + set_fan_swing_vertical_service, + schema=SET_FAN_SWING_VERTICAL_SCHEMA, + ) + def neviweb_to_ha(value): keys = [k for k, v in HA_TO_NEVIWEB_PERIOD.items() if v == value] if keys: @@ -1754,6 +1794,32 @@ def set_sound_config(self, value): entity, sound) self._sound_conf = sound + def set_fan_swing_horizontal(self, value): + """Set horizontal fan swing action for heat pump.""" + swing = value["swing"] + entity = value["id"] + if swing not in extract_capability_full(self._fan_swing_cap_horiz): + self.notify_ha( + f"Warning: Value selected for fan swing horizontal is not supported by " + self._name + ) + return + self._client.set_swing_horizontal( + entity, swing) + self._fan_swing_horiz = swing + + def set_fan_swing_vertical(self, value): + """Set vertical fan swing action for heat pump.""" + swing = value["swing"] + entity = value["id"] + if swing not in extract_capability_full(self._fan_swing_cap_vert): + self.notify_ha( + f"Warning: Value selected for fan swing vertical is not supported by " + self._name + ) + return + self._client.set_swing_vertical( + entity, swing) + self._fan_swing_vert = swing + def do_stat(self, start): """Get device energy statistic.""" if start - self._energy_stat_time > STAT_INTERVAL and self._energy_stat_time != 0: From e83c1b39cccceaabb1f718dd4910b8ec2b8a0421 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 22:09:09 -0500 Subject: [PATCH 22/31] Add fan set_swing... services --- custom_components/neviweb130/const.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/custom_components/neviweb130/const.py b/custom_components/neviweb130/const.py index e85298d..69ca067 100644 --- a/custom_components/neviweb130/const.py +++ b/custom_components/neviweb130/const.py @@ -281,3 +281,5 @@ SERVICE_SET_HEAT_LOCKOUT_TEMPERATURE = "set_heat_lockout_temperature" SERVICE_SET_DISPLAY_CONFIG = "set_display_config" SERVICE_SET_SOUND_CONFIG = "set_sound_config" +SERVICE_SET_FAN_SWING_VERTICAL = "set_fan_swing_vertical" +SERVICE_SET_FAN_SWING_HORIZONTAL = "set_fan_swing_horizontal" From 32fb5a3fd4bb46e891c7921ef870dd9b36ca4936 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 22:13:32 -0500 Subject: [PATCH 23/31] Add fan set_swing...schema --- custom_components/neviweb130/schema.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/custom_components/neviweb130/schema.py b/custom_components/neviweb130/schema.py index 05d4551..2ca9fd0 100644 --- a/custom_components/neviweb130/schema.py +++ b/custom_components/neviweb130/schema.py @@ -32,6 +32,8 @@ ATTR_DISPLAY_CONF, ATTR_DRACTIVE, ATTR_EARLY_START, + ATTR_FAN_SWING_HORIZ, + ATTR_FAN_SWING_VERT, ATTR_FLOOR_AIR_LIMIT, ATTR_FLOOR_MAX, ATTR_FLOOR_MIN, @@ -107,6 +109,10 @@ FAN_SWING_CAPABILITY = {"fullHorizontal", "autoHorizontal", "fullVertical", "autoVertical"} DISPLAY_CAPABILITY = {"enable", "disable"} SOUND_CAPABILITY = {"enable", "disable"} +SWING_CAPABILITY_VERTICAL = {'swingFullRange', 'off', 'fixedRegion1', 'fixedRegion2', 'fixedRegion3', 'fixedRegion4', 'fixedRegion5', 'fixedRegion6', 'fixedRegion7', 'fixedRegion8', + 'swingRegion1','swingRegion2','swingRegion3','swingRegion3','swingRegion5','swingRegion6','swingRegion7','swingRegion8'} +SWING_CAPABILITY_HORIZONTAL = {'swingFullRange', 'off', 'fixedRegion1', 'fixedRegion2', 'fixedRegion3', 'fixedRegion4', 'fixedRegion5', 'fixedRegion6', 'fixedRegion7', 'fixedRegion8', + 'swingRegion1','swingRegion2','swingRegion3','swingRegion3','swingRegion5','swingRegion6','swingRegion7','swingRegion8'} """Config schema.""" @@ -367,6 +373,24 @@ } ) +SET_FAN_SWING_VERTICAL_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_FAN_SWING_VERT): vol.All( + cv.ensure_list, [vol.In(SWING_CAPABILITY_VERTICAL)] + ), + } +) + +SET_FAN_SWING_HORIZONTALL_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_id, + vol.Required(ATTR_FAN_SWING_HORIZ): vol.All( + cv.ensure_list, [vol.In(SWING_CAPABILITY_HORIZONTAL)] + ), + } +) + """light schema.""" SET_LIGHT_KEYPAD_LOCK_SCHEMA = vol.Schema( From dc68af7c3ca6930e4703140bfbb45b87ce8a73c2 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Sun, 17 Nov 2024 22:16:15 -0500 Subject: [PATCH 24/31] Add description for fan set_swing... services --- custom_components/neviweb130/services.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/custom_components/neviweb130/services.yaml b/custom_components/neviweb130/services.yaml index e1d5ec0..3dfef7f 100644 --- a/custom_components/neviweb130/services.yaml +++ b/custom_components/neviweb130/services.yaml @@ -652,3 +652,23 @@ set_sound_config: soundConfig: description: possible values, enabled, disabled. example: "disabled" + +set_fan_swing_horizontal: + description: Set horizontal fan swing action for heat pump. + fields: + entity_id: + description: Name(s) of neviweb130 thermostat HP6000 device. + example: "climate.neviweb130_climate_HP6000ZB-GE" + fanSwingHorizontal: + description: For possible values, check fan_swing_capability_vertical attribute of your device. + example: "swingFullRange" + +set_fan_swing_vertical: + description: Set vertical fan swing action for heat pump. + fields: + entity_id: + description: Name(s) of neviweb130 thermostat HP6000 device. + example: "climate.neviweb130_climate_HP6000ZB-GE" + fanSwingVertical: + description: For possible values, check fan_swing_capability_horizontal attribute of your device. + example: "swingFullRange" From 735886900e6c60699ab994fdd28c275514a4dcda Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Mon, 18 Nov 2024 16:31:11 -0500 Subject: [PATCH 25/31] Add fan_modes and swing_modes --- custom_components/neviweb130/climate.py | 84 ++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 14d6176..10f40a5 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -57,6 +57,10 @@ HVACMode, ) from homeassistant.components.climate.const import ( + ATTR_FAN_MODES, + ATTR_FAN_MODE, + ATTR_SWING_MODES, + ATTR_SWING_MODE, PRESET_AWAY, PRESET_HOME, PRESET_NONE, @@ -275,8 +279,17 @@ SUPPORT_HP_FLAGS = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE -# | ClimateEntityFeature.FAN_MODE -# | ClimateEntityFeature.SWING_MODE + | ClimateEntityFeature.FAN_MODE + | ClimateEntityFeature.SWING_MODE + | ClimateEntityFeature.TURN_OFF + | ClimateEntityFeature.TURN_ON +) + +SUPPORT_Hc_FLAGS = ( + ClimateEntityFeature.TARGET_TEMPERATURE + | ClimateEntityFeature.PRESET_MODE + | ClimateEntityFeature.FAN_MODE + | ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON ) @@ -284,8 +297,7 @@ SUPPORT_HC_FLAGS = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE -# | ClimateEntityFeature.FAN_MODE -# | ClimateEntityFeature.SWING_MODE + | ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON ) @@ -1273,8 +1285,10 @@ def supported_features(self): return SUPPORT_AUX_FLAGS elif self._is_HP: return SUPPORT_HP_FLAGS - elif self._is_HC or self._is_hc: + elif self._is_HC: return SUPPORT_HC_FLAGS + elif self._is_hc: + return SUPPORT_Hc_FLAGS else: return SUPPORT_FLAGS @@ -1440,6 +1454,58 @@ def is_on(self): return True return False + @property + def fan_modes(self): + """Return available fan modes.""" + if self._is_HP or self._is_HC or self._is_hc: + return FAN_SPEED + else: + return None + + @property + def fan_mode(self): + """Return the fan setting.""" + return self._fan_speed + + @property + def swing_modes(self): + """Return available swing modes""" + if self._is_HP or self._is_hc: + return extract_capability(self._fan_swing_cap) + else: + return None + + @property + def swing_mode(self): + """Return the fan swing setting.""" + return self._fan_swing_vert + + @property + def swing_modes_vertical(self): + """Return available swing modes""" + if self._is_HP or self._is_hc: + return extract_capability_full(self._fan_swing_cap_vert) + else: + return None + + @property + def swing_mode_vertical(self): + """Return the fan swing setting.""" + return self._fan_swing_vert + + @property + def swing_modes_horizontal(self): + """Return available swing modes""" + if self._is_HP or self._is_hc: + return extract_capability_full(self._fan_swing_cap_horiz) + else: + return None + + @property + def swing_mode_horizontal(self): + """Return the fan swing setting.""" + return self._fan_swing_horiz + def turn_on(self): """Turn the thermostat to HVACMode.heat on.""" self._client.set_setpoint_mode(self._id, HVACMode.HEAT, self._is_wifi) @@ -3464,14 +3530,14 @@ def extra_state_attributes(self): 'fan_speed': self._fan_speed, 'fan_swing_vertical': self._fan_swing_vert, 'fan_swing_horizontal': self._fan_swing_horiz, - 'fan_capability': self._fan_cap, + 'fan_capability': extract_capability(self._fan_cap), 'fan_swing_capability': self._fan_swing_cap, - 'fan_swing_capability_vertical': self._fan_swing_cap_vert, - 'fan_swing_capability_horizontal': self._fan_swing_cap_horiz, + 'fan_swing_capability_vertical': extract_capability_full(self._fan_swing_cap_vert), + 'fan_swing_capability_horizontal': extract_capability_full(self._fan_swing_cap_horiz), 'display_conf': self._display_conf, 'display_capability': self._display_cap, 'sound_conf': self._sound_conf, - 'sound_capability': self._sound_cap, + 'sound_capability': extract_capability(self._sound_cap), 'balance_point': self._balance_pt, 'heat_lock_temp': self._heat_lock_temp, 'cool_lock_temp': self._cool_lock_temp, From fc99be13a8533fab298a3e5cc78812cf9d022db2 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Mon, 18 Nov 2024 22:14:01 -0500 Subject: [PATCH 26/31] Add set fan and swing mode --- custom_components/neviweb130/climate.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 10f40a5..69c89a9 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -1506,6 +1506,22 @@ def swing_mode_horizontal(self): """Return the fan swing setting.""" return self._fan_swing_horiz + def set_fan_mode(self, **kwargs): + """Set new fan mode.""" + speed = kwargs.get(ATTR_FAN_MODE) + if speed is None: + return + self._client.set_fan_mode(self._id, speed) + self._fan_speed = speed + + def set_swing_mode(self, **kwargs): + """Set new swing mode.""" + swing = kwargs.get(ATTR_SWING_MODE) + if swing is None: + return + self._client.set_swing_vertical(self._id, swing) + self._fan_swing_vert = swing + def turn_on(self): """Turn the thermostat to HVACMode.heat on.""" self._client.set_setpoint_mode(self._id, HVACMode.HEAT, self._is_wifi) From 1c3f271f54d44affc7913ef92db657d242476a53 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Mon, 18 Nov 2024 22:18:51 -0500 Subject: [PATCH 27/31] Add set_fan_mode --- custom_components/neviweb130/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/custom_components/neviweb130/__init__.py b/custom_components/neviweb130/__init__.py index 6d76c2d..edfc1ee 100644 --- a/custom_components/neviweb130/__init__.py +++ b/custom_components/neviweb130/__init__.py @@ -44,6 +44,7 @@ ATTR_DRSETPOINT, ATTR_DRSTATUS, ATTR_EARLY_START, + ATTR_FAN_SPEED, ATTR_FAN_SWING_HORIZ, ATTR_FAN_SWING_VERT, ATTR_FLOOR_AUX, @@ -1004,6 +1005,12 @@ def set_swing_vertical(self, device_id, swing): _LOGGER.debug("Fan vertical swing value.data = %s", data) self.set_device_attributes(device_id, data) + def set_fan_mode(self, device_id, speed): + """Set fan speed (mode) for heat pump.""" + data = {ATTR_FAN_SPEED: speed} + _LOGGER.debug("Fan speed value.data = %s", data) + self.set_device_attributes(device_id, data) + def set_device_attributes(self, device_id, data): """Ser devices attributes.""" result = 1 From 25976904508170908fb51bd167064867f3cd12f9 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Tue, 19 Nov 2024 10:25:50 -0500 Subject: [PATCH 28/31] Fix argument number for fan and swing --- custom_components/neviweb130/climate.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 69c89a9..6b698e5 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -1506,17 +1506,15 @@ def swing_mode_horizontal(self): """Return the fan swing setting.""" return self._fan_swing_horiz - def set_fan_mode(self, **kwargs): + def set_fan_mode(self, speed): """Set new fan mode.""" - speed = kwargs.get(ATTR_FAN_MODE) if speed is None: return self._client.set_fan_mode(self._id, speed) self._fan_speed = speed - def set_swing_mode(self, **kwargs): + def set_swing_mode(self, swing): """Set new swing mode.""" - swing = kwargs.get(ATTR_SWING_MODE) if swing is None: return self._client.set_swing_vertical(self._id, swing) From e217d79e951c6886f2995a9ac2c32136d4efc31d Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Tue, 19 Nov 2024 14:32:51 -0500 Subject: [PATCH 29/31] Fix set_swing values --- custom_components/neviweb130/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index 6b698e5..cdd05f2 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -1471,7 +1471,7 @@ def fan_mode(self): def swing_modes(self): """Return available swing modes""" if self._is_HP or self._is_hc: - return extract_capability(self._fan_swing_cap) + return extract_capability_full(self._fan_swing_cap_vert) else: return None From a903767f56da2a24318ed2c0ad2ef821d96f1835 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Thu, 21 Nov 2024 20:59:09 -0500 Subject: [PATCH 30/31] Add support for horizontal swing modes --- custom_components/neviweb130/climate.py | 80 +++++++++++++++---------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/custom_components/neviweb130/climate.py b/custom_components/neviweb130/climate.py index cdd05f2..f959638 100644 --- a/custom_components/neviweb130/climate.py +++ b/custom_components/neviweb130/climate.py @@ -355,7 +355,10 @@ ATTR_TIME, ] -FULL_SWING = ['swingFullRange', 'off'] +FULL_SWING = ['swingFullRange'] +FULL_SWING_HORIZ = ['swingFullRangeHoriz'] +FULL_SWING_OFF = ['off'] +FULL_SWING_OFF_HORIZ = ['offHoriz'] SUPPORTED_HVAC_WIFI_MODES = [ HVACMode.AUTO, @@ -1071,10 +1074,13 @@ def lock_to_ha(lock): case "partialLock": return "Tamper protection" -def extract_capability_full(cap): - """Extract swing capability which are True for each HP device.""" - value = {i for i in cap if cap[i]==True} - return FULL_SWING + sorted(value) +def extract_capability_full(sens, cap): + """Extract swing capability which are True for each HP device and add denegal capability.""" + if sens == "vert": + value = {i for i in cap if cap[i]==True} + return FULL_SWING_OFF + sorted(value) + value = {i+"Horiz" for i in cap if cap[i]==True} + return FULL_SWING_OFF_HORIZ + sorted(value) def extract_capability(cap): """Extract capability which are True for each HP device.""" @@ -1469,9 +1475,9 @@ def fan_mode(self): @property def swing_modes(self): - """Return available swing modes""" + """Return available swing modes, vertical + horizontal.""" if self._is_HP or self._is_hc: - return extract_capability_full(self._fan_swing_cap_vert) + return self.swing_modes_vertical + self.swing_modes_horizontal else: return None @@ -1482,9 +1488,12 @@ def swing_mode(self): @property def swing_modes_vertical(self): - """Return available swing modes""" + """Return available vertical swing modes""" if self._is_HP or self._is_hc: - return extract_capability_full(self._fan_swing_cap_vert) + if "fullVertical" in extract_capability(self._fan_swing_cap): + return FULL_SWING + extract_capability_full("vert", self._fan_swing_cap_vert) + else: + return extract_capability_full("vert", self._fan_swing_cap_vert) else: return None @@ -1495,9 +1504,12 @@ def swing_mode_vertical(self): @property def swing_modes_horizontal(self): - """Return available swing modes""" + """Return available horizontal swing modes""" if self._is_HP or self._is_hc: - return extract_capability_full(self._fan_swing_cap_horiz) + if "fullHorizontal" in extract_capability(self._fan_swing_cap): + return FULL_SWING_HORIZ + extract_capability_full("horiz", self._fan_swing_cap_horiz) + else: + return extract_capability_full("horiz", self._fan_swing_cap_horiz) else: return None @@ -1517,8 +1529,12 @@ def set_swing_mode(self, swing): """Set new swing mode.""" if swing is None: return - self._client.set_swing_vertical(self._id, swing) - self._fan_swing_vert = swing + elif swing[-5:] == "Horiz": + self._client.set_swing_horizontal(self._id, swing[:-5]) + self._fan_swing_horiz = swing[:-5] + else: + self._client.set_swing_vertical(self._id, swing) + self._fan_swing_vert = swing def turn_on(self): """Turn the thermostat to HVACMode.heat on.""" @@ -1878,26 +1894,24 @@ def set_fan_swing_horizontal(self, value): """Set horizontal fan swing action for heat pump.""" swing = value["swing"] entity = value["id"] - if swing not in extract_capability_full(self._fan_swing_cap_horiz): - self.notify_ha( - f"Warning: Value selected for fan swing horizontal is not supported by " + self._name - ) - return - self._client.set_swing_horizontal( - entity, swing) - self._fan_swing_horiz = swing + if swing[-5:] == "Horiz": + if swing not in FULL_SWING_HORIZ + extract_capability_full("horiz", self._fan_swing_cap_horiz): + self.notify_ha(f"Warning: Value " + swing + "selected for fan swing horizontal is not supported by " + self._name) + return + self._client.set_swing_horizontal(self._id, swing[:-5]) + self._fan_swing_horiz = swing[:-5] + else: + self._client.set_swing_horizontal(entity, swing) + self._fan_swing_horiz = swing def set_fan_swing_vertical(self, value): """Set vertical fan swing action for heat pump.""" swing = value["swing"] entity = value["id"] - if swing not in extract_capability_full(self._fan_swing_cap_vert): - self.notify_ha( - f"Warning: Value selected for fan swing vertical is not supported by " + self._name - ) + if swing not in FULL_SWING + extract_capability_full("vert", self._fan_swing_cap_vert): + self.notify_ha(f"Warning: Value selected for fan swing vertical is not supported by " + self._name) return - self._client.set_swing_vertical( - entity, swing) + self._client.set_swing_vertical(entity, swing) self._fan_swing_vert = swing def do_stat(self, start): @@ -3545,11 +3559,11 @@ def extra_state_attributes(self): 'fan_swing_vertical': self._fan_swing_vert, 'fan_swing_horizontal': self._fan_swing_horiz, 'fan_capability': extract_capability(self._fan_cap), - 'fan_swing_capability': self._fan_swing_cap, - 'fan_swing_capability_vertical': extract_capability_full(self._fan_swing_cap_vert), - 'fan_swing_capability_horizontal': extract_capability_full(self._fan_swing_cap_horiz), + 'fan_swing_capability': extract_capability(self._fan_swing_cap), + 'fan_swing_capability_vertical': extract_capability_full("vert", self._fan_swing_cap_vert), + 'fan_swing_capability_horizontal': extract_capability_full("horiz", self._fan_swing_cap_horiz), 'display_conf': self._display_conf, - 'display_capability': self._display_cap, + 'display_capability': extract_capability(self._display_cap), 'sound_conf': self._sound_conf, 'sound_capability': extract_capability(self._sound_cap), 'balance_point': self._balance_pt, @@ -3752,8 +3766,8 @@ def extra_state_attributes(self): 'fan_swing_horizontal': self._fan_swing_horiz, 'fan_capability': self._fan_cap, 'fan_swing_capability': extract_capability(self._fan_swing_cap), - 'fan_swing_capability_vertical': extract_capability_full(self._fan_swing_cap_vert), - 'fan_swing_capability_horizontal': extract_capability_full(self._fan_swing_cap_horiz), + 'fan_swing_capability_vertical': extract_capability_full("vert", self._fan_swing_cap_vert), + 'fan_swing_capability_horizontal': extract_capability_full("horiz", self._fan_swing_cap_horiz), 'heat_pump_limit_temp': self._balance_pt, 'min_heat_pump_limit_temp': self._balance_pt_low, 'max_heat_pump_limit_temp': self._balance_pt_high, From 0cf868cf8cf724cef287f489cb3722e08b2ad789 Mon Sep 17 00:00:00 2001 From: Claude Gelinas Date: Fri, 22 Nov 2024 20:29:14 -0500 Subject: [PATCH 31/31] Add heatpump services --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 43ff298..69410da 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,13 @@ Automations require services to be able to send commande. Ex. light.turn_on. For - neviweb130.set_remaining_time to set value for coldLoadPickupRemainingTime attribute. - neviweb130.set_on_off_input_delay to set the «on» or «off» delay in seconds for input 1 and 2 of MC3100ZB. - neviweb130.set_em_heat to turn on/off aux heat for floor and low voltage thermostats. This is a replacement of turn_aux_heat_on or off that was deprecated by HA. +- neviweb130.set_display_config to set on/off display on heatpump. +- neviweb130.set_sound_config to set on/off sound on heatpump. +- neviweb130.set_fan_swing_horizontal to set various vertical fan swing modes for heatpump. +- neviweb130.set_fan_swing_vertical to set various horizontal swing modes for heatpump. +- neviweb130.set_heat_pump_operation_limit to set minimum operation temperature for heatpump. +- neviweb130.set_heat_lockout_temperature to set maximum outside temperature limit to allow heating device operation. +- neviweb130.set_cool_lockout_temperature to set minimum outside temperature limit to allow cooling device operation. ## Catch Éco Sinopé signal for peak period If you have at least on thermostat or one load controler registered with Éco-Sinopé program, it is now possible to catch when Neviweb send the signal for pre-heating start period for thermostats or turn_off signal for the load controler. Seven attributes have been added for thermostats and three for load controler to know that peak period is comming and how it is managed: