From 2fc2c56d92b4ae704b10b162c420725370c68d18 Mon Sep 17 00:00:00 2001 From: Indu Prakash Date: Fri, 25 Oct 2024 20:07:00 -0500 Subject: [PATCH 1/3] fix: report fan circulation mode correctly Fan mode will be circulate if it is auto andcirculating_fan is enabled. --- custom_components/sensi/coordinator.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/custom_components/sensi/coordinator.py b/custom_components/sensi/coordinator.py index c827c6a..7744690 100644 --- a/custom_components/sensi/coordinator.py +++ b/custom_components/sensi/coordinator.py @@ -33,6 +33,7 @@ HVAC_MODE_TO_OPERATING_MODE, LOGGER, OPERATING_MODE_TO_HVAC_MODE, + SENSI_FAN_AUTO, SENSI_FAN_CIRCULATE, Capabilities, OperatingModes, @@ -192,7 +193,7 @@ def update(self, data_json: dict): self.cool_target = state.get("current_cool_temp") self.heat_target = state.get("current_heat_temp") - # Fan mode is on or auto. We will create a third mode circulate which is based on auto. + # Fan mode is reported as 'on' or 'auto' if "fan_mode" in state: self.fan_mode = state.get("fan_mode") @@ -207,7 +208,12 @@ def update(self, data_json: dict): "duty_cycle" ] - if self.attributes[ATTR_CIRCULATING_FAN] == "on": + # Create a third mode 'circulate' base on 'auto' when 'circulating_fan' + # contains 'enabled'='on'. + if ( + self.fan_mode == SENSI_FAN_AUTO + and self.attributes[ATTR_CIRCULATING_FAN] == "on" + ): self.fan_mode = SENSI_FAN_CIRCULATE for key in Settings: From 9cadccaea14329322fda2f9abf510fda10ef3776 Mon Sep 17 00:00:00 2001 From: Indu Prakash Date: Fri, 25 Oct 2024 20:29:36 -0500 Subject: [PATCH 2/3] style: added unit test --- custom_components/sample.json | 108 ------------------------ tests/__init__.py | 1 + tests/conftest.py | 31 +++++++ tests/sample.json | 152 ++++++++++++++++++++++++++++++++++ tests/test_sensi_device.py | 10 +++ 5 files changed, 194 insertions(+), 108 deletions(-) delete mode 100644 custom_components/sample.json create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/sample.json create mode 100644 tests/test_sensi_device.py diff --git a/custom_components/sample.json b/custom_components/sample.json deleted file mode 100644 index 7a1a854..0000000 --- a/custom_components/sample.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "status": "online", - "current_cool_temp": 77, - "current_heat_temp": 68, - "display_temp": 68, - "current_operating_mode": "heat", - "humidity": 44, - "battery_voltage": 2.98, - "power_status": "c_wire", - "wifi_connection_quality": 58, - "periodicity": 0, - "comfort_alert": "None", - "other_error_bitfield": { - "bad_temperature_sensor": "off", - "bad_humidity_sensor": "off", - "stuck_key": "off", - "high_voltage": "off", - "e5_alert": "off", - "error_32": "off", - "error_64": "off" - }, - "current_humidification_percent": 5, - "current_dehumidification_percent": 40, - "relay_status": { - "w": "on", - "w2": "off", - "g": "on", - "y": "off", - "y2": "off", - "o_b": "off" - }, - "demand_status": { - "heat": 50, - "fan": 100, - "cool": 0, - "aux": 0, - "last": "heat", - "last_start": 1670495953, - "cool_stage": "None", - "heat_stage": 1, - "aux_stage": "None", - "humidification": 0, - "dehumidification": 0, - "overcooling": "no" - }, - "hashedSchedule": "e1207cd23f7cedd8e53bfd4ce0e8881388efb0c0", - "display_scale": "f", - "heat_max_temp": 99, - "cool_min_temp": 45, - "hold_mode": "off", - "operating_mode": "heat", - "scheduling": "off", - "fan_mode": "on", - "display_humidity": "on", - "continuous_backlight": "off", - "compressor_lockout": "on", - "early_start": "off", - "keypad_lockout": "off", - "temp_offset": 0, - "aux_cycle_rate": "medium", - "cool_cycle_rate": "medium", - "heat_cycle_rate": "medium", - "aux_boost": "on", - "heat_boost": "off", - "cool_boost": "off", - "dst_offset": 60, - "dst_observed": "yes", - "tz_offset": -360, - "hold_end": "None", - "deadband": 2, - "display_time": "on", - "circulating_fan": { - "enabled": "off", - "duty_cycle": 10 - }, - "humidity_offset": 0, - "partial_keypad_lockout": { - "setpoint": "on", - "system_mode": "on", - "fan_mode": "on", - "schedule_mode": "on", - "settings_menu": "on" - }, - "humidity_control": { - "humidification": { - "target_percent": 5, - "enabled": "off", - "mode": "humidifier" - }, - "dehumidification": { - "target_percent": 40, - "enabled": "off", - "mode": "overcooling" - }, - "status": "None" - }, - "lcd_sleep_mode": "None", - "night_light": "None", - "outdoor_weather_display": "ff:00:00:ff:ff:ff:00:00:ff:00:00:ff:00:00:ff:00:00:ff:00", - "geofencing": "None", - "remote_sensor_status": "00", - "control": { - "mode": "off", - "devices": "None", - "geo_state": "None", - "device_data": "None" - } -} diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..92ecaf2 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for Sensi component.""" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..08180ca --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,31 @@ +import json +import os + +import pytest + +from custom_components.sensi.auth import AuthenticationConfig +from custom_components.sensi.coordinator import SensiUpdateCoordinator +from homeassistant.core import HomeAssistant + + +def load_json(filename): + """Load sample JSON.""" + path = os.path.join(os.path.dirname(__file__), filename) + with open(path, encoding="utf-8") as fptr: + return fptr.read() + + +@pytest.fixture(name="mock_coordinator") +def create_mock_crumb_coordinator(hass: HomeAssistant) -> SensiUpdateCoordinator: + """Fixture to provide a test instance of CrumbCoordinator.""" + config = AuthenticationConfig() + config.access_token = "access_token" + config.expires_at = 12345 + config.refresh_token = "refresh_token" + return SensiUpdateCoordinator(hass, config) + + +@pytest.fixture +def mock_json(): + """Return sample JSON data.""" + return json.loads(load_json("sample.json")) diff --git a/tests/sample.json b/tests/sample.json new file mode 100644 index 0000000..d0ad9a2 --- /dev/null +++ b/tests/sample.json @@ -0,0 +1,152 @@ +{ + "icd_id": "36-6f-92-ff-fe-0c-0b-07", + "registration": { + "city": "Madison", + "name": "Living Room", + "state": "Wisconsin", + "country": "US", + "address1": "Somewhere", + "address2": null, + "timezone": "America/Chicago", + "postal_code": "53719", + "product_type": "Sensi Classic with HomeKit", + "contractor_id": null, + "fleet_enabled": false, + "fleet_enabled_date": null + }, + "capabilities": { + "keypad_lockout": "yes", + "continuous_backlight": "yes", + "degrees_fc": "yes", + "display_humidity": "yes", + "display_time": "yes", + "circulating_fan": { + "capable": "yes", + "max_duty_cycle": 100, + "min_duty_cycle": 10, + "step": 5 + }, + "operating_mode_settings": { + "off": "yes", + "heat": "yes", + "cool": "yes", + "aux": "no", + "auto": "yes" + }, + "fan_mode_settings": { + "auto": "yes", + "on": "yes", + "smart": "no" + } + }, + "state": { + "status": "online", + "current_cool_temp": 76, + "current_heat_temp": 68, + "display_temp": 68.5, + "current_operating_mode": "heat", + "humidity": 62, + "battery_voltage": 2.981, + "power_status": "c_wire", + "wifi_connection_quality": 46, + "periodicity": 0, + "comfort_alert": null, + "other_error_bitfield": { + "bad_temperature_sensor": "off", + "bad_humidity_sensor": "off", + "stuck_key": "off", + "high_voltage": "off", + "e5_alert": "off", + "error_32": "off", + "error_64": "off" + }, + "current_humidification_percent": 5, + "current_dehumidification_percent": 40, + "relay_status": { + "w": "off", + "w2": "off", + "g": "off", + "y": "off", + "y2": "off", + "o_b": "off" + }, + "demand_status": { + "heat": 0, + "fan": 0, + "cool": 0, + "aux": 0, + "last": "heat", + "last_start": null, + "cool_stage": null, + "heat_stage": null, + "aux_stage": null, + "humidification": 0, + "dehumidification": 0, + "overcooling": "no" + }, + "hashedSchedule": "e1207cd23f7cedd8e53bfd4ce0e8881388efb0c0", + "current_off_temp": 60, + "display_scale": "f", + "heat_max_temp": 72, + "cool_min_temp": 68, + "hold_mode": "off", + "operating_mode": "heat", + "scheduling": "off", + "fan_mode": "on", + "display_humidity": "on", + "continuous_backlight": "off", + "compressor_lockout": "on", + "early_start": "off", + "keypad_lockout": "off", + "temp_offset": 0, + "aux_cycle_rate": "medium", + "cool_cycle_rate": "medium", + "heat_cycle_rate": "medium", + "aux_boost": "on", + "heat_boost": "off", + "cool_boost": "off", + "dst_offset": 60, + "dst_observed": "yes", + "tz_offset": -360, + "hold_end": null, + "deadband": 2, + "display_time": "on", + "circulating_fan": { + "enabled": "on", + "duty_cycle": 10 + }, + "humidity_offset": 0, + "partial_keypad_lockout": { + "setpoint": "on", + "system_mode": "on", + "fan_mode": "on", + "schedule_mode": "on", + "settings_menu": "on" + }, + "humidity_control": { + "humidification": { + "target_percent": 5, + "enabled": "off", + "mode": "humidifier" + }, + "dehumidification": { + "target_percent": 40, + "enabled": "off", + "mode": "overcooling" + }, + "status": "none" + }, + "lcd_sleep_mode": null, + "night_light": null, + "outdoor_weather_display": "ff:00:00:ff:ff:ff:00:00:ff:00:00:ff:00:00:ff:00:00:ff:00", + "geofencing": null, + "remote_sensor_status": "00", + "target_off_temp": 60, + "control": { + "mode": "off", + "devices": null, + "geo_state": null, + "device_data": null + } + } +} \ No newline at end of file diff --git a/tests/test_sensi_device.py b/tests/test_sensi_device.py new file mode 100644 index 0000000..866a9f8 --- /dev/null +++ b/tests/test_sensi_device.py @@ -0,0 +1,10 @@ +"""Tests for SensiDevice.""" + +from custom_components.sensi.const import SENSI_FAN_ON +from custom_components.sensi.coordinator import SensiDevice + + +def test_update(mock_coordinator, mock_json) -> None: + """Test update of SensiDevice.""" + device = SensiDevice(mock_coordinator, mock_json) + assert device.fan_mode == SENSI_FAN_ON From 751a64e00af8209f39bf6ea9b53e3903d595e3fa Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 26 Oct 2024 01:44:01 +0000 Subject: [PATCH 3/3] chore: bumping version to 1.3.14 --- custom_components/sensi/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/sensi/manifest.json b/custom_components/sensi/manifest.json index 3921533..491e797 100644 --- a/custom_components/sensi/manifest.json +++ b/custom_components/sensi/manifest.json @@ -6,7 +6,7 @@ "dependencies": [], "codeowners": ["@iprak"], "issue_tracker": "https://github.com/iprak/sensi/issues", - "version": "1.3.13", + "version": "1.3.14", "config_flow": true, "iot_class": "cloud_polling" }