diff --git a/.vscode/launch.json b/.vscode/launch.json index fbc0b20..4bae663 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,11 +1,17 @@ { - "version": "0.2.0", "configurations": [ - { - "name": "Debug test", - "type": "python", - "request": "attach", - "justMyCode": false - } - ] + { + "name": "Python: Debug Tests", + "type": "python", + "request": "launch", + "program": "${file}", + "purpose": ["debug-test"], + "console": "integratedTerminal", + "justMyCode": false, + "presentation": { + "hidden": true, // keep original launch order in 'run and debug' tab + } + }, + ], + "version": "0.2.0" } diff --git a/.vscode/settings.json b/.vscode/settings.json index 25602b3..4d9ab92 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,10 @@ { - "python.testing.pytestArgs": ["tests", "--asyncio-mode=auto"], + "python.testing.pytestArgs": [ + "tests","--asyncio-mode=auto" + ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, + "python.experiments.enabled": false, "cSpell.words": [ "ASHRAE", "automations", @@ -26,5 +29,6 @@ "python.linting.flake8Enabled": false, "python.linting.enabled": true, "python.linting.flake8Args": ["--config=setup.cfg", "--doctests"], - "python.linting.pylintEnabled": true + "python.linting.pylintEnabled": true, + } diff --git a/README.MD b/README.MD index 1cb41aa..ce0f5f9 100644 --- a/README.MD +++ b/README.MD @@ -29,6 +29,7 @@ Cloud Connections depending on the device model. We believe these configurations - Passwords with special characters are not properly supported by the Lennox Cloud API and may not work. Known special characters that cause issues are & and ^. If your password contains these characters you may need to change it prior to using the integration. - Adjusting the fan CFM in a single zone system causes some diagnostics to stop updating. Changing the diag_level to 0 and back to 2 solves the issue. A script is available [here](./samples/set_fan_cfm) - A power outage to the Lennox Controller causes the diag_level to be reset to 0 on reboot. An automation/package is available [here](./samples/diaglevel) to automatically reset. +- If you add new equipment (BLE Sensors), restart HA in order for the integration to pickup the new equipment. # Planning your install @@ -58,43 +59,9 @@ If you like this integration HACS is recommended as it provides automated install and will notify you when updates are available. -This assumes you have [HACS](https://github.com/hacs/integration) installed and know how to use it. If you need help with this, go to the HACS project documentation. This documentation is based on HACS 1.23.0 - other versions may have different UI. +This assumes you have [HACS](https://github.com/hacs/integration) installed and know how to use it. If you need help with this, go to the HACS project documentation. -Add custom repository in _HACS_ - -1. Click on HACS in your menu to open the HACS panel, then click on integrations (https://your.domain/hacs/integrations). -1. Click on the 3 dots in the top right corner. -1. Select "Custom repositories" -1. Add the URL to the repository: `https://github.com/PeteRager/lennoxs30` -1. Select the integration category. -1. Click the "ADD" button. - -Once done, you should see the new repository, appearing in a list like this. Click the **Download** button - -![plot](./doc_images/hacs_1.PNG) - -After clicking download, you will be prompted for which version to download. Select the latest - which will be the default - and click download. - -![plot](./doc_images/hacs_2.PNG) - -Once the download is complete, you will see that to make the component available requires a restart. Restart Home Assistant. - -![plot](./doc_images/hacs_3.PNG) - -Once Home Assistant restarts and you navigate to HACS. This screen should appear showing the component is added. - -![plot](./doc_images/hacs_4.PNG) - -Next go to Home Assistant / Configuration / Integrations and select add integration. Search for lennox. Select the integration and follow the [Configuration Steps](#Configuration) - -![plot](./doc_images/hacs_5.PNG) - -Alternate HACS instructions for old versions - Add integration in _HACS_ - -1. In the HACS panel, go to integrations and click the '+ Explore & add repositories' button. -1. Search for `Lennox iComfort WiFi Thermostat Integration` and click `Install this repository in HACS`. -1. Restart HA to load the integration into HA.
-1. Follow the Configuration Instructions below +As of October 2023, this integration is included in the HACS default configuration. ## Manually @@ -489,6 +456,21 @@ Example JSON ] ``` +### wifi_rssi + +Local Connections Only. Reports the WIFI signal strength, this sensor can be used to diagnose WIFI dropout issues. + + Attribute | Type | Description | +| -------------------------- | ----------- | ------------------------------------------------------------------- | +| macAddr | string | MAC Address of the controller | +| ssid | string | WIFI network name | +| ip | string | IP Address of the controller | +| router | string | IP Address of the router gateway | +| dns | string | DNS IP Address | +| dns2 | string | Secondary DNS IP address | +| subnetMask | string | LAN subnet mask | +| bitrate | float | WIFI connection bitrate | + ## Select Entities ### Humidity Mode diff --git a/custom_components/lennoxs30/binary_sensor.py b/custom_components/lennoxs30/binary_sensor.py index aeb61ae..2dd3dd4 100644 --- a/custom_components/lennoxs30/binary_sensor.py +++ b/custom_components/lennoxs30/binary_sensor.py @@ -12,8 +12,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.components.binary_sensor import ( - DEVICE_CLASS_PRESENCE, - DEVICE_CLASS_CONNECTIVITY, + BinarySensorDeviceClass, BinarySensorEntity, ) @@ -181,7 +180,7 @@ def device_info(self) -> DeviceInfo: @property def device_class(self): - return DEVICE_CLASS_PRESENCE + return BinarySensorDeviceClass.PRESENCE class S30InternetStatus(S30BaseEntityMixin, BinarySensorEntity): @@ -241,7 +240,7 @@ def device_info(self) -> DeviceInfo: @property def device_class(self): - return DEVICE_CLASS_CONNECTIVITY + return BinarySensorDeviceClass.CONNECTIVITY @property def entity_category(self): @@ -304,7 +303,7 @@ def device_info(self) -> DeviceInfo: @property def device_class(self): - return DEVICE_CLASS_CONNECTIVITY + return BinarySensorDeviceClass.CONNECTIVITY @property def entity_category(self): @@ -376,7 +375,7 @@ def device_info(self) -> DeviceInfo: @property def device_class(self): - return DEVICE_CLASS_CONNECTIVITY + return BinarySensorDeviceClass.CONNECTIVITY @property def entity_category(self): diff --git a/custom_components/lennoxs30/climate.py b/custom_components/lennoxs30/climate.py index ccbea68..1124e77 100644 --- a/custom_components/lennoxs30/climate.py +++ b/custom_components/lennoxs30/climate.py @@ -25,8 +25,7 @@ ) from homeassistant.const import ( ATTR_TEMPERATURE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.config_entries import ConfigEntry @@ -225,8 +224,8 @@ def supported_features(self): def temperature_unit(self): """Return the unit of measurement.""" if self._manager.is_metric is False: - return TEMP_FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.CELSIUS @property def min_temp(self): @@ -237,7 +236,7 @@ def min_temp(self): if self._manager.is_metric is False: return self._zone.minCsp return self._zone.minCspC - if self._zone.systemMode == LENNOX_HVAC_HEAT: + if self._zone.systemMode in [LENNOX_HVAC_HEAT,LENNOX_HVAC_EMERGENCY_HEAT]: if self._manager.is_metric is False: return self._zone.minHsp return self._zone.minHspC @@ -264,7 +263,7 @@ def max_temp(self): if self._manager.is_metric is False: return self._zone.maxCsp return self._zone.maxCspC - if self._zone.systemMode == LENNOX_HVAC_HEAT: + if self._zone.systemMode in [LENNOX_HVAC_HEAT,LENNOX_HVAC_EMERGENCY_HEAT]: if self._manager.is_metric is False: return self._zone.maxHsp return self._zone.maxHspC diff --git a/custom_components/lennoxs30/const.py b/custom_components/lennoxs30/const.py index 506876b..adb18ab 100644 --- a/custom_components/lennoxs30/const.py +++ b/custom_components/lennoxs30/const.py @@ -46,6 +46,7 @@ UNIQUE_ID_SUFFIX_BLE: Final = "_BLE" UNIQUE_ID_SUFFIX_BLE_COMMSTATUS: Final = "_BLE_COMMSTATUS" UNIQUE_ID_SUFFIX_VENTILATION_SELECT: Final = "_VENT_SELECT" +UNIQUE_ID_SUFFIX_WIFI_RSSI: Final = "_WIFI_RSSI" VENTILATION_EQUIPMENT_ID = -900 diff --git a/custom_components/lennoxs30/diagnostics.py b/custom_components/lennoxs30/diagnostics.py index 158c84b..f7f709c 100644 --- a/custom_components/lennoxs30/diagnostics.py +++ b/custom_components/lennoxs30/diagnostics.py @@ -1,4 +1,4 @@ -"""Diagnostics support for Nest.""" +"""Diagnostics support for LennoxS30.""" # pylint: disable=line-too-long from __future__ import annotations from typing import Any diff --git a/custom_components/lennoxs30/manifest.json b/custom_components/lennoxs30/manifest.json index 049de41..4eae49d 100644 --- a/custom_components/lennoxs30/manifest.json +++ b/custom_components/lennoxs30/manifest.json @@ -8,6 +8,6 @@ "iot_class": "local_push", "issue_tracker" : "https://github.com/PeteRager/lennoxs30/issues", "quality_scale": "platinum", - "requirements": ["lennoxs30api==0.2.10"], - "version": "2023.10.1" + "requirements": ["lennoxs30api==0.2.12"], + "version": "2024.1.1" } \ No newline at end of file diff --git a/custom_components/lennoxs30/number.py b/custom_components/lennoxs30/number.py index f0dfe8a..144f7ab 100644 --- a/custom_components/lennoxs30/number.py +++ b/custom_components/lennoxs30/number.py @@ -12,9 +12,8 @@ from homeassistant.components.number import NumberEntity from homeassistant.const import ( PERCENTAGE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TIME_MINUTES, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.helpers import config_validation as cv from homeassistant.exceptions import HomeAssistantError @@ -234,8 +233,8 @@ def name(self): @property def native_unit_of_measurement(self): if self._manager.is_metric is False: - return TEMP_FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.CELSIUS @property def native_max_value(self) -> float: @@ -427,7 +426,7 @@ async def async_set_native_value(self, value: float) -> None: @property def native_unit_of_measurement(self): - return TIME_MINUTES + return UnitOfTime.MINUTES @property def device_info(self) -> DeviceInfo: diff --git a/custom_components/lennoxs30/sensor.py b/custom_components/lennoxs30/sensor.py index a9c2e2a..8eddc4f 100644 --- a/custom_components/lennoxs30/sensor.py +++ b/custom_components/lennoxs30/sensor.py @@ -48,9 +48,9 @@ from .ble_device_21p02 import lennox_21p02_sensors, lennox_iaq_sensors from .sensor_ble import S40BleSensor from .sensor_iaq import S40IAQSensor +from .sensor_wifi import WifiRSSISensor from .sensor_wt_env import lennox_wt_env_sensors, WTEnvSensor, lennox_wt_env_sensors_metric, lennox_wt_env_sensors_us - _LOGGER = logging.getLogger(__name__) DOMAIN = "lennoxs30" @@ -175,6 +175,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e ble_device.controlModelNumber, ) + if manager.api.isLANConnection: + sensor_list.append(WifiRSSISensor(hass, manager, system)) + if len(sensor_list) != 0: async_add_entities(sensor_list, True) return True diff --git a/custom_components/lennoxs30/sensor_wifi.py b/custom_components/lennoxs30/sensor_wifi.py new file mode 100644 index 0000000..6105653 --- /dev/null +++ b/custom_components/lennoxs30/sensor_wifi.py @@ -0,0 +1,92 @@ +"""Support for Lennoxs30 outdoor temperature sensor""" +# pylint: disable=global-statement +# pylint: disable=broad-except +# pylint: disable=unused-argument +# pylint: disable=line-too-long +# pylint: disable=invalid-name +import logging +from typing import Any + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT +from homeassistant.helpers.entity import EntityCategory + +from lennoxs30api import lennox_system + +from . import Manager +from .base_entity import S30BaseEntityMixin +from .const import LENNOX_DOMAIN, UNIQUE_ID_SUFFIX_WIFI_RSSI + +_LOGGER = logging.getLogger(__name__) + +class WifiRSSISensor(S30BaseEntityMixin, SensorEntity): + """Class for Lennox S40 WTEnvSensor Sensors.""" + + def __init__(self, hass: HomeAssistant, manager: Manager, system: lennox_system): + super().__init__(manager, system) + self._hass = hass + self._myname = self._system.name + "_wifi_rssi" + + async def async_added_to_hass(self) -> None: + """Run when entity about to be added to hass.""" + _LOGGER.debug("async_added_to_hass WifiRSSISensor myname [%s]", self._myname) + self._system.registerOnUpdateCallback( + self.update_callback, + ["wifi_rssi", "wifi_macAddr", "wifi_ssid", "wifi_ip", "wifi_router","wifi_dns","wifi_dns2","wifi_subnetMask","wifi_bitRate"], + ) + await super().async_added_to_hass() + + def update_callback(self): + """Callback to execute on data change""" + _LOGGER.debug("update_callback WifiRSSISensor myname [%s]", self._myname) + self.schedule_update_ha_state() + + @property + def unique_id(self) -> str: + return (self._system.unique_id + UNIQUE_ID_SUFFIX_WIFI_RSSI).replace("-", "") + + @property + def extra_state_attributes(self): + """Return the state attributes.""" + attrs: dict[str, Any] = {} + attrs["macAddr"] = self._system.wifi_macAddr + attrs["ssid"] = self._system.wifi_ssid + attrs["ip"] = self._system.wifi_ip + attrs["router"] = self._system.wifi_router + attrs["dns"] = self._system.wifi_dns + attrs["dns2"] = self._system.wifi_dns2 + attrs["subnetMask"] = self._system.wifi_subnetMask + attrs["bitRate"] = self._system.wifi_bitRate + return attrs + @property + def name(self): + return self._myname + + @property + def native_value(self): + return self._system.wifi_rssi + + @property + def native_unit_of_measurement(self): + return SIGNAL_STRENGTH_DECIBELS_MILLIWATT + + @property + def device_class(self): + return SensorDeviceClass.SIGNAL_STRENGTH + + @property + def state_class(self): + return SensorStateClass.MEASUREMENT + + @property + def device_info(self) -> DeviceInfo: + return { + "identifiers": {(LENNOX_DOMAIN, self._system.unique_id)}, + } + + @property + def entity_category(self): + return EntityCategory.DIAGNOSTIC diff --git a/custom_components/lennoxs30/strings.json b/custom_components/lennoxs30/strings.json index 0713ec7..28edc99 100644 --- a/custom_components/lennoxs30/strings.json +++ b/custom_components/lennoxs30/strings.json @@ -27,9 +27,9 @@ "app_id": "Unique application id to use. See documentation", "init_wait_time": "Configuration Wait time in seconds", "create_sensors": "Temperature and Humidity Sensors", - "create_inverter_power": "WARN! - Inverter Power Sensor", - "create_diagnostic_sensors": "WARN!- Diagnostic Sensors", - "create_parameters": "BETA - Equipment Parameters", + "create_inverter_power": "Inverter Power Sensor", + "create_diagnostic_sensors": "Diagnostic Sensors", + "create_parameters": "Equipment Parameters", "protocol": "Should be HTTPS unless using simulator" } }, @@ -66,9 +66,9 @@ "allergen_defender_switch": "Create Allergen Defender switch", "app_id": "Unique application id to use. See documentation.", "create_sensors": "Temperature and humidity Sensors", - "create_inverter_power": "WARN! - Inverter power sensor", - "create_diagnostic_sensors": "WARN!- Diagnostic Sensors", - "create_parameters": "BETA - Equipment Parameters", + "create_inverter_power": "Inverter power sensor", + "create_diagnostic_sensors": "Diagnostic Sensors", + "create_parameters": "Equipment Parameters", "protocol": "Should be HTTPS unless using simulator", "scan_interval": "Message scan interval seconds.", "fast_scan_interval": "Fast scan interval seconds. 0.75 recommended", diff --git a/custom_components/lennoxs30/translations/en.json b/custom_components/lennoxs30/translations/en.json index c6b3776..a2a6802 100644 --- a/custom_components/lennoxs30/translations/en.json +++ b/custom_components/lennoxs30/translations/en.json @@ -27,9 +27,9 @@ "app_id": "Unique application id to use. See documentation", "init_wait_time": "Configuration Wait time in seconds", "create_sensors": "Temperature and Humidity Sensors", - "create_inverter_power": "WARN!- Inverter Power Sensor", - "create_diagnostic_sensors": "WARN!- Diagnostic Sensors", - "create_parameters": "BETA - Equipment Parameters", + "create_inverter_power": "Inverter Power Sensor", + "create_diagnostic_sensors": "Diagnostic Sensors", + "create_parameters": "Equipment Parameters", "protocol": "Should be HTTPS unless using simulator" } }, @@ -68,9 +68,9 @@ "allergen_defender_switch": "Create allergenDefender switch", "app_id": "Unique application id to use. See documentation", "create_sensors": "Temperature and humidity Sensors", - "create_inverter_power": "WARN!- Inverter power sensor", - "create_diagnostic_sensors": "WARN!- Diagnostic Sensors", - "create_parameters": "BETA - Equipment Parameters", + "create_inverter_power": "Inverter power sensor", + "create_diagnostic_sensors": "Diagnostic Sensors", + "create_parameters": "Equipment Parameters", "protocol": "Should be HTTPS unless using simulator", "scan_interval": "Message scan interval seconds.", "fast_scan_interval": "Fast scan interval seconds. 0.75 recommended", diff --git a/doc_images/hacs_1.PNG b/doc_images/hacs_1.PNG deleted file mode 100644 index aadea0f..0000000 Binary files a/doc_images/hacs_1.PNG and /dev/null differ diff --git a/doc_images/hacs_2.PNG b/doc_images/hacs_2.PNG deleted file mode 100644 index 6504dc8..0000000 Binary files a/doc_images/hacs_2.PNG and /dev/null differ diff --git a/doc_images/hacs_3.PNG b/doc_images/hacs_3.PNG deleted file mode 100644 index 329a033..0000000 Binary files a/doc_images/hacs_3.PNG and /dev/null differ diff --git a/doc_images/hacs_4.PNG b/doc_images/hacs_4.PNG deleted file mode 100644 index 12a0beb..0000000 Binary files a/doc_images/hacs_4.PNG and /dev/null differ diff --git a/doc_images/hacs_5.PNG b/doc_images/hacs_5.PNG deleted file mode 100644 index 0dd688c..0000000 Binary files a/doc_images/hacs_5.PNG and /dev/null differ diff --git a/doc_images/hacs_6.PNG b/doc_images/hacs_6.PNG deleted file mode 100644 index d7e0298..0000000 Binary files a/doc_images/hacs_6.PNG and /dev/null differ diff --git a/docs/diagnostics.md b/docs/diagnostics.md index 5d9273f..36aa69a 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -1,8 +1,10 @@ # Lennox S30 Diagnostic Configuration -**WARNING** improperly configuring diagnostics can cause system stability issues - including excessive S30 controller reboots. communication instability and loss of control. +## Updates 2024-01 -In order to use diagnostics, your network should be setup to prevent the S30 from communicating to the Lennox Cloud. This will require router and firewall configuration. +**WARNING** improperly configuring diagnostics may cause system stability issues - including excessive controller reboots. communication instability and loss of control. As of Jan 2024, Lennox firmware updates have improved the stability (or at least we are receiving less reports of issues) + +In order to use diagnostics, your network should be setup to prevent the S30 from communicating to the Lennox Cloud. This will require router and firewall configuration. When diagnostics is enabled the data updates every 4 seconds and will be sent to the integration and the Lennox Cloud. If you are using the integration for the first time, the recommendation is to get your system working without diagnostics for a week, to prove it is stable. @@ -24,6 +26,36 @@ For us Home Assistant enthusiasts, this primary use case is real-time power cons Enabling diagnostic is done by setting the system diagnostic level from its default 0 to 2. When enabled, the system sends an update every 4 seconds containing the sensor data. This is sent to every connected client, which includes this integration, the lennox cloud and the Lennox App on your phone. This is where the problems occur. We believe when there is not be enough bandwidth to push the diagnostic data to the Lennox Cloud, across fhe cellular network to the phone apps. This causes data to start backing up in the S30 and eventually it exceeds a limit (runs out of memory?) and reboots. When it is rebooting your HVAC is off. Once it comes back up, the cycle repeats. +Note: As of December 2023 on S40s that are internet connected, it appears that Lennox is automatically disabling diagnostics after a timeout. This is likely to prevent excessive data hitting their cloud. This behavior has not been reported on S40s that are not internet connected. While the **official** recommendation is to not operate diagnsotics with a cloud connected system. If you are cloud connected and see this behavior the following script has been shown to automatically correct the issue. + +``` +alias: Fix Lennox S40 Diagnostics +description: "" +trigger: + - platform: time_pattern + minutes: "0" + - platform: time_pattern + minutes: "15" + - platform: time_pattern + minutes: "30" + - platform: time_pattern + minutes: "45" +condition: [] +action: + - service: number.set_value + data: + value: "1" + target: + entity_id: number.lennox_diagnostic_level + enabled: true + - service: number.set_value + data: + value: "2" + target: + entity_id: number.lennox_diagnostic_level +mode: single +``` + It is certainly possible that it can be stable with an internet connected phone app. All we do know, is many stability issues have occurred when enabling diagnostic with internet connected S30s. And the only known stable systems are internet isolated. ## Blocking the internet diff --git a/docs/parameters.md b/docs/parameters.md index 0da5c72..cb7f74f 100644 --- a/docs/parameters.md +++ b/docs/parameters.md @@ -1,4 +1,4 @@ -# Lennox S30 Equipment Parameters Beta +# Lennox S30 Equipment Parameters **WARNING** improperly setting equipment parameters could render your HVAC system inoperable - please use caution and write down old values before changing and review this document. diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..9e098ae --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --allow-unix-socket \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 6dd4dad..d04bcaa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -76,6 +76,10 @@ pytest_plugins = "pytest_homeassistant_custom_component" +@pytest.fixture(autouse=True) +def socket_enabled(): + pass + @pytest.fixture(autouse=True) def auto_enable_custom_integrations(enable_custom_integrations): yield diff --git a/tests/messages/wifi_interface_status.json b/tests/messages/wifi_interface_status.json new file mode 100644 index 0000000..ad92976 --- /dev/null +++ b/tests/messages/wifi_interface_status.json @@ -0,0 +1,48 @@ +{ + "MessageId": 0, + "SenderID": "**redacted**", + "TargetID": "homeassistant", + "MessageType": "PropertyChange", + "Data": { + "interfaces": [ + { + "publisher": { + "publisherName": "lcc" + }, + "Info": { + "status": { + "macAddr": "60:a4:4c:6b:d2:4c", + "ssid": "wifi_home", + "ip": "10.0.0.10", + "router": "10.0.0.1", + "networkStatus": "enabled", + "channel": 0, + "dns": "8.8.8.8", + "dns2": "4.4.4.4", + "subnetMask": "255.255.0.0", + "bitRate": 72200000, + "speed": " 72.2 MBit per second", + "rssi": -68 + }, + "APDetails": { + "rssi": -68, + "security": "WPA-Personal", + "password": "**redacted**", + "ssid": "**redacted**", + "bssid": "60:a4:4c:6b:d2:4c" + }, + "diagnostics": { + "ssid": "**redacted**", + "bssid": "60:a4:4c:6b:d2:4c", + "txByteCount": 75453148, + "rxByteCount": 0, + "ip4addr": "**redacted**", + "txPacketCount": 157356, + "rxPacketCount": 0 + } + }, + "id": 0 + } + ] + } +} diff --git a/tests/test_binary_sensor_ble_commstatus.py b/tests/test_binary_sensor_ble_commstatus.py index 195ab5b..33cae4e 100644 --- a/tests/test_binary_sensor_ble_commstatus.py +++ b/tests/test_binary_sensor_ble_commstatus.py @@ -3,7 +3,7 @@ import logging from unittest.mock import patch -from homeassistant.components.binary_sensor import DEVICE_CLASS_CONNECTIVITY +from homeassistant.components.binary_sensor import BinarySensorDeviceClass from lennoxs30api.s30api_async import lennox_system, LENNOX_BLE_COMMSTATUS_AVAILABLE, LennoxBle import pytest @@ -45,7 +45,7 @@ async def test_binary_sensor_ble_commstatus(hass, manager_system_04_furn_ac_zoni assert attrs["commStatus"] == "BAD_STATUS" assert sensor.entity_category == "diagnostic" - assert sensor.device_class == DEVICE_CLASS_CONNECTIVITY + assert sensor.device_class == BinarySensorDeviceClass.CONNECTIVITY identifiers = sensor.device_info["identifiers"] for element in identifiers: diff --git a/tests/test_binary_sensor_cloud_connection_status.py b/tests/test_binary_sensor_cloud_connection_status.py index 0a2bbe8..e0a9992 100644 --- a/tests/test_binary_sensor_cloud_connection_status.py +++ b/tests/test_binary_sensor_cloud_connection_status.py @@ -16,7 +16,7 @@ from unittest.mock import patch from homeassistant.components.binary_sensor import ( - DEVICE_CLASS_CONNECTIVITY, + BinarySensorDeviceClass, ) @@ -43,7 +43,7 @@ async def test_cloud_connected_status_init(hass, manager: Manager, caplog): assert c.available == False assert c.entity_category == "diagnostic" - assert c.device_class == DEVICE_CLASS_CONNECTIVITY + assert c.device_class == BinarySensorDeviceClass.CONNECTIVITY identifiers = c.device_info["identifiers"] for x in identifiers: diff --git a/tests/test_binary_sensor_internet_status.py b/tests/test_binary_sensor_internet_status.py index f0ab68f..c819cd0 100644 --- a/tests/test_binary_sensor_internet_status.py +++ b/tests/test_binary_sensor_internet_status.py @@ -13,7 +13,7 @@ from unittest.mock import patch from homeassistant.components.binary_sensor import ( - DEVICE_CLASS_CONNECTIVITY, + BinarySensorDeviceClass, ) from tests.conftest import conftest_base_entity_availability @@ -31,7 +31,7 @@ async def test_internet_status_init(hass, manager: Manager, caplog): assert system.internetStatus == None assert c.available == False assert c.entity_category == "diagnostic" - assert c.device_class == DEVICE_CLASS_CONNECTIVITY + assert c.device_class == BinarySensorDeviceClass.CONNECTIVITY identifiers = c.device_info["identifiers"] for x in identifiers: diff --git a/tests/test_binary_sensor_relay_service_status.py b/tests/test_binary_sensor_relay_service_status.py index 75a69f1..31aeacc 100644 --- a/tests/test_binary_sensor_relay_service_status.py +++ b/tests/test_binary_sensor_relay_service_status.py @@ -13,7 +13,7 @@ from unittest.mock import patch from homeassistant.components.binary_sensor import ( - DEVICE_CLASS_CONNECTIVITY, + BinarySensorDeviceClass, ) from tests.conftest import conftest_base_entity_availability @@ -31,7 +31,7 @@ async def test_relay_service_status_init(hass, manager: Manager, caplog): assert system.relayServerConnected == None assert c.available == False assert c.entity_category == "diagnostic" - assert c.device_class == DEVICE_CLASS_CONNECTIVITY + assert c.device_class == BinarySensorDeviceClass.CONNECTIVITY identifiers = c.device_info["identifiers"] for x in identifiers: diff --git a/tests/test_climate.py b/tests/test_climate.py index 88670fc..afab797 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -29,8 +29,7 @@ SUPPORT_TARGET_TEMPERATURE_RANGE, ) from homeassistant.const import ( - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature ) @@ -94,7 +93,7 @@ async def test_climate_min_max_c(hass, manager_mz: Manager): # Metric Tests assert manager.is_metric is True - assert c.temperature_unit == TEMP_CELSIUS + assert c.temperature_unit == UnitOfTemperature.CELSIUS zone.systemMode = LENNOX_HVAC_OFF assert c.min_temp is None assert c.max_temp is None @@ -107,6 +106,10 @@ async def test_climate_min_max_c(hass, manager_mz: Manager): assert c.min_temp == zone.minHspC assert c.max_temp == zone.maxHspC + zone.systemMode = LENNOX_HVAC_EMERGENCY_HEAT + assert c.min_temp == zone.minHspC + assert c.max_temp == zone.maxHspC + assert system.single_setpoint_mode is True zone.systemMode = LENNOX_HVAC_HEAT_COOL assert c.min_temp == zone.minCspC @@ -141,7 +144,7 @@ async def test_climate_min_max_f(hass, manager_mz: Manager, caplog): manager.is_metric = False assert manager.is_metric is False - assert c.temperature_unit == TEMP_FAHRENHEIT + assert c.temperature_unit == UnitOfTemperature.FAHRENHEIT zone.systemMode = LENNOX_HVAC_OFF assert c.min_temp is None assert c.max_temp is None @@ -154,6 +157,10 @@ async def test_climate_min_max_f(hass, manager_mz: Manager, caplog): assert c.min_temp == zone.minHsp assert c.max_temp == zone.maxHsp + zone.systemMode = LENNOX_HVAC_EMERGENCY_HEAT + assert c.min_temp == zone.minHsp + assert c.max_temp == zone.maxHsp + assert system.single_setpoint_mode is True zone.systemMode = LENNOX_HVAC_HEAT_COOL assert c.min_temp == zone.minCsp diff --git a/tests/test_diag_sensor.py b/tests/test_diag_sensor.py index 7281be5..e0eb470 100644 --- a/tests/test_diag_sensor.py +++ b/tests/test_diag_sensor.py @@ -11,12 +11,12 @@ from homeassistant.const import ( PERCENTAGE, - TEMP_FAHRENHEIT, + UnitOfTemperature, FREQUENCY_HERTZ, ELECTRIC_CURRENT_AMPERE, VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, ELECTRIC_POTENTIAL_VOLT, - TIME_MINUTES, + UnitOfTime, REVOLUTIONS_PER_MINUTE, ) from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorDeviceClass @@ -169,13 +169,13 @@ async def test_diag_sensor_unit_of_measure_device_class(hass, manager: Manager): equipment = system.equipment[1] diagnostic = equipment.diagnostics[9] s = S30DiagSensor(hass, manager, system, equipment, diagnostic) - assert s.native_unit_of_measurement == TEMP_FAHRENHEIT + assert s.native_unit_of_measurement == UnitOfTemperature.FAHRENHEIT assert s.device_class == SensorDeviceClass.TEMPERATURE equipment = system.equipment[1] diagnostic = equipment.diagnostics[12] s = S30DiagSensor(hass, manager, system, equipment, diagnostic) - assert s.unit_of_measurement == TIME_MINUTES + assert s.unit_of_measurement == UnitOfTime.MINUTES assert s.device_class is None equipment = system.equipment[1] diff --git a/tests/test_helpers.py b/tests/test_helpers.py index f3789c0..5dfdc89 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -1,16 +1,15 @@ -from logging import ERROR, WARNING +"""Test for the helper module""" +# pylint: disable=line-too-long import logging import pytest from homeassistant.const import ( PERCENTAGE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - FREQUENCY_HERTZ, - ELECTRIC_CURRENT_AMPERE, - VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, - ELECTRIC_POTENTIAL_VOLT, - TIME_MINUTES, - TIME_SECONDS, + UnitOfTemperature, + UnitOfFrequency, + UnitOfElectricCurrent, + UnitOfVolumeFlowRate, + UnitOfElectricPotential, + UnitOfTime ) from custom_components.lennoxs30 import Manager from custom_components.lennoxs30.const import LENNOX_DOMAIN @@ -20,25 +19,26 @@ helper_get_equipment_device_info, lennox_uom_to_ha_uom, ) -from custom_components.lennoxs30.number import EquipmentParameterNumber def test_helpers_lennox_uom_to_ha_uom(): - assert lennox_uom_to_ha_uom("F") == TEMP_FAHRENHEIT - assert lennox_uom_to_ha_uom("C") == TEMP_CELSIUS - assert lennox_uom_to_ha_uom("CFM") == VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE - assert lennox_uom_to_ha_uom("min") == TIME_MINUTES - assert lennox_uom_to_ha_uom("sec") == TIME_SECONDS + """Test the conversion of unit from lennox to HA""" + assert lennox_uom_to_ha_uom("F") == UnitOfTemperature.FAHRENHEIT + assert lennox_uom_to_ha_uom("C") == UnitOfTemperature.CELSIUS + assert lennox_uom_to_ha_uom("CFM") == UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE + assert lennox_uom_to_ha_uom("min") == UnitOfTime.MINUTES + assert lennox_uom_to_ha_uom("sec") == UnitOfTime.SECONDS assert lennox_uom_to_ha_uom("%") == PERCENTAGE - assert lennox_uom_to_ha_uom("Hz") == FREQUENCY_HERTZ - assert lennox_uom_to_ha_uom("V") == ELECTRIC_POTENTIAL_VOLT - assert lennox_uom_to_ha_uom("A") == ELECTRIC_CURRENT_AMPERE - assert lennox_uom_to_ha_uom("") == None + assert lennox_uom_to_ha_uom("Hz") == UnitOfFrequency.HERTZ + assert lennox_uom_to_ha_uom("V") == UnitOfElectricPotential.VOLT + assert lennox_uom_to_ha_uom("A") == UnitOfElectricCurrent.AMPERE + assert lennox_uom_to_ha_uom("") is None assert lennox_uom_to_ha_uom("my_custom_unit") == "my_custom_unit" @pytest.mark.asyncio async def test_helpers_helper_get_equipment_device_info(manager: Manager): + """Test the helper to create device info""" await manager.create_devices() system = manager.api.system_list[0] device_info = helper_get_equipment_device_info(manager, system, 1) @@ -51,6 +51,7 @@ async def test_helpers_helper_get_equipment_device_info(manager: Manager): @pytest.mark.asyncio async def test_helpers_helper_get_equipment_device_info_no_system(manager: Manager, caplog): + """Test the helper to create device info""" system = manager.api.system_list[0] with caplog.at_level(logging.ERROR): caplog.clear() @@ -67,6 +68,7 @@ async def test_helpers_helper_get_equipment_device_info_no_system(manager: Manag @pytest.mark.asyncio async def test_helpers_helper_get_equipment_device_info_no_device(manager: Manager, caplog): + """Test the helper to create device info""" await manager.create_devices() system = manager.api.system_list[0] manager.system_equip_device_map[system.sysId] = {} @@ -84,7 +86,8 @@ async def test_helpers_helper_get_equipment_device_info_no_device(manager: Manag @pytest.mark.asyncio -async def test_helpers_create_equipment_entity_name(manager: Manager, caplog): +async def test_helpers_create_equipment_entity_name(manager: Manager): + """Test the helper to create device info""" await manager.create_devices() system = manager.api.system_list[0] equipment = system.equipment[0] diff --git a/tests/test_number_dehumidification_overcool.py b/tests/test_number_dehumidification_overcool.py index da85ae5..1320461 100644 --- a/tests/test_number_dehumidification_overcool.py +++ b/tests/test_number_dehumidification_overcool.py @@ -9,8 +9,7 @@ import pytest from homeassistant.const import ( - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature ) from lennoxs30api.s30api_async import ( @@ -53,9 +52,9 @@ async def test_dehumd_overcool_unique_id_unit_of_measure(hass, manager: Manager) system: lennox_system = manager.api.system_list[0] manager.is_metric = True c = DehumidificationOverCooling(hass, manager, system) - assert c.unit_of_measurement == TEMP_CELSIUS + assert c.unit_of_measurement == UnitOfTemperature.CELSIUS manager.is_metric = False - assert c.unit_of_measurement == TEMP_FAHRENHEIT + assert c.unit_of_measurement == UnitOfTemperature.FAHRENHEIT @pytest.mark.asyncio diff --git a/tests/test_number_equipment_parameter_number.py b/tests/test_number_equipment_parameter_number.py index b626048..99a903b 100644 --- a/tests/test_number_equipment_parameter_number.py +++ b/tests/test_number_equipment_parameter_number.py @@ -8,7 +8,7 @@ from unittest.mock import patch import pytest -from homeassistant.const import TEMP_FAHRENHEIT +from homeassistant.const import UnitOfTemperature from homeassistant.exceptions import HomeAssistantError from lennoxs30api.s30api_async import lennox_system @@ -54,7 +54,7 @@ async def test_equipment_parameter_number_unit_of_measure(hass, manager: Manager equipment = system.equipment[0] parameter = equipment.parameters[72] c = EquipmentParameterNumber(hass, manager, system, equipment, parameter) - assert c.unit_of_measurement == TEMP_FAHRENHEIT + assert c.unit_of_measurement == UnitOfTemperature.FAHRENHEIT @pytest.mark.asyncio diff --git a/tests/test_number_timed_ventilation.py b/tests/test_number_timed_ventilation.py index 3c740bf..30dc150 100644 --- a/tests/test_number_timed_ventilation.py +++ b/tests/test_number_timed_ventilation.py @@ -9,7 +9,7 @@ from unittest.mock import patch import pytest -from homeassistant.const import TIME_MINUTES +from homeassistant.const import UnitOfTime from homeassistant.exceptions import HomeAssistantError from lennoxs30api.s30api_async import lennox_system @@ -47,7 +47,7 @@ async def test_timed_ventilation_time_name(hass, manager: Manager): async def test_timed_ventilation_time_unit_of_measure(hass, manager: Manager): system: lennox_system = manager.api.system_list[0] c = TimedVentilationNumber(hass, manager, system) - assert c.unit_of_measurement == TIME_MINUTES + assert c.unit_of_measurement == UnitOfTime.MINUTES @pytest.mark.asyncio diff --git a/tests/test_outdoor_temperature_sensor.py b/tests/test_outdoor_temperature_sensor.py index ad00cec..2e72aa9 100644 --- a/tests/test_outdoor_temperature_sensor.py +++ b/tests/test_outdoor_temperature_sensor.py @@ -9,8 +9,8 @@ from unittest.mock import patch import pytest -from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, DEVICE_CLASS_TEMPERATURE +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorDeviceClass +from homeassistant.const import UnitOfTemperature from lennoxs30api.s30api_async import ( LENNOX_STATUS_GOOD, @@ -41,12 +41,12 @@ async def test_outdoor_temperature_sensor(hass, manager: Manager, caplog): assert len(s.extra_state_attributes) == 0 manager.is_metric = False assert s.native_value == system.outdoorTemperature - assert s.native_unit_of_measurement == TEMP_FAHRENHEIT + assert s.native_unit_of_measurement == UnitOfTemperature.FAHRENHEIT manager.is_metric = True assert s.native_value == system.outdoorTemperatureC - assert s.native_unit_of_measurement == TEMP_CELSIUS + assert s.native_unit_of_measurement == UnitOfTemperature.CELSIUS - assert s.device_class == DEVICE_CLASS_TEMPERATURE + assert s.device_class == SensorDeviceClass.TEMPERATURE assert s.state_class == STATE_CLASS_MEASUREMENT identifiers = s.device_info["identifiers"] diff --git a/tests/test_sensor_humidity.py b/tests/test_sensor_humidity.py index 7cf7331..fb466cf 100644 --- a/tests/test_sensor_humidity.py +++ b/tests/test_sensor_humidity.py @@ -9,8 +9,8 @@ from unittest.mock import patch import pytest -from homeassistant.const import PERCENTAGE, DEVICE_CLASS_HUMIDITY -from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT +from homeassistant.const import PERCENTAGE +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorDeviceClass from lennoxs30api.s30api_async import ( LENNOX_BAD_STATUS, @@ -42,7 +42,7 @@ async def test_humidity_sensor(hass, manager: Manager, caplog): assert s.state == zone.humidity assert s.unit_of_measurement == PERCENTAGE - assert s.device_class == DEVICE_CLASS_HUMIDITY + assert s.device_class == SensorDeviceClass.HUMIDITY assert s.state_class == STATE_CLASS_MEASUREMENT identifiers = s.device_info["identifiers"] diff --git a/tests/test_sensor_power_inverter.py b/tests/test_sensor_power_inverter.py index db59906..88077ab 100644 --- a/tests/test_sensor_power_inverter.py +++ b/tests/test_sensor_power_inverter.py @@ -17,10 +17,10 @@ S30InverterPowerSensor, ) -from homeassistant.const import TEMP_CELSIUS, POWER_WATT, DEVICE_CLASS_POWER +from homeassistant.const import POWER_WATT from homeassistant.components.sensor import ( - STATE_CLASS_MEASUREMENT, + STATE_CLASS_MEASUREMENT, SensorDeviceClass ) @@ -68,7 +68,7 @@ async def test_power_inverter_sensor(hass, manager: Manager, caplog): assert s.unit_of_measurement == POWER_WATT - assert s.device_class == DEVICE_CLASS_POWER + assert s.device_class == SensorDeviceClass.POWER assert s.state_class == STATE_CLASS_MEASUREMENT identifiers = s.device_info["identifiers"] diff --git a/tests/test_sensor_setup.py b/tests/test_sensor_setup.py index 5b19af7..a55a0d7 100644 --- a/tests/test_sensor_setup.py +++ b/tests/test_sensor_setup.py @@ -27,6 +27,7 @@ ) from custom_components.lennoxs30.sensor_ble import S40BleSensor from custom_components.lennoxs30.sensor_iaq import S40IAQSensor +from custom_components.lennoxs30.sensor_wifi import WifiRSSISensor from custom_components.lennoxs30.sensor_wt_env import WTEnvSensor from tests.conftest import loadfile @@ -46,10 +47,20 @@ async def test_async_setup_entry(hass, manager: Manager, caplog): manager.create_sensors = False manager.create_diagnostic_sensors = False manager.create_alert_sensors = False + manager.api.isLANConnection = False async_add_entities = Mock() await async_setup_entry(hass, entry, async_add_entities) assert async_add_entities.called == 0 + manager.api.isLANConnection = True + async_add_entities = Mock() + await async_setup_entry(hass, entry, async_add_entities) + assert async_add_entities.called == 1 + sensor_list = async_add_entities.call_args[0][0] + assert len(sensor_list) == 1 + assert isinstance(sensor_list[0], WifiRSSISensor) + manager.api.isLANConnection = False + # Outdoor Temperature Sensor system.outdoorTemperatureStatus = LENNOX_STATUS_GOOD manager.create_inverter_power = False diff --git a/tests/test_sensor_temperature.py b/tests/test_sensor_temperature.py index adc1d91..1e4d3a3 100644 --- a/tests/test_sensor_temperature.py +++ b/tests/test_sensor_temperature.py @@ -9,8 +9,8 @@ from unittest.mock import patch import pytest -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, DEVICE_CLASS_TEMPERATURE -from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT +from homeassistant.const import UnitOfTemperature +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorDeviceClass from lennoxs30api.s30api_async import ( LENNOX_BAD_STATUS, @@ -41,12 +41,12 @@ async def test_temperature_sensor(hass, manager: Manager, caplog): manager.is_metric = False assert s.native_value == zone.temperature - assert s.native_unit_of_measurement == TEMP_FAHRENHEIT + assert s.native_unit_of_measurement == UnitOfTemperature.FAHRENHEIT manager.is_metric = True assert s.native_value == zone.temperatureC - assert s.native_unit_of_measurement == TEMP_CELSIUS + assert s.native_unit_of_measurement == UnitOfTemperature.CELSIUS - assert s.device_class == DEVICE_CLASS_TEMPERATURE + assert s.device_class == SensorDeviceClass.TEMPERATURE assert s.state_class == STATE_CLASS_MEASUREMENT identifiers = s.device_info["identifiers"] diff --git a/tests/test_sensor_wifi_rssi.py b/tests/test_sensor_wifi_rssi.py new file mode 100644 index 0000000..89acad3 --- /dev/null +++ b/tests/test_sensor_wifi_rssi.py @@ -0,0 +1,133 @@ +# pylint: disable=too-many-lines +# pylint: disable=missing-module-docstring +# pylint: disable=missing-function-docstring +# pylint: disable=invalid-name +# pylint: disable=protected-access +# pylint: disable=line-too-long + +import logging +from unittest.mock import patch +import pytest + +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorDeviceClass +from homeassistant.const import UnitOfTemperature,SIGNAL_STRENGTH_DECIBELS_MILLIWATT + +from lennoxs30api.s30api_async import lennox_system + +from custom_components.lennoxs30 import Manager +from custom_components.lennoxs30.const import LENNOX_DOMAIN +from custom_components.lennoxs30.sensor_wifi import WifiRSSISensor + +from tests.conftest import conftest_base_entity_availability, loadfile + + +@pytest.mark.asyncio +async def test_wifi_rssi_sensor(hass, manager: Manager, caplog): + manager.is_metric = False + system: lennox_system = manager.api.system_list[0] + message = loadfile("wifi_interface_status.json", system.sysId) + system.processMessage(message) + + s = WifiRSSISensor(hass, manager, system) + + assert s.unique_id == (system.unique_id + "_WIFI_RSSI").replace("-", "") + assert s.available is True + assert s.should_poll is False + assert s.update() is True + assert s.name == system.name + "_wifi_rssi" + assert len(s.extra_state_attributes) == 8 + attrs = s.extra_state_attributes + assert attrs["macAddr"] == "60:a4:4c:6b:d2:4c" + assert attrs["ssid"] == "wifi_home" + assert attrs["ip"] == "10.0.0.10" + assert attrs["router"] == "10.0.0.1" + assert attrs["dns"] == "8.8.8.8" + assert attrs["dns2"] == "4.4.4.4" + assert attrs["subnetMask"] == "255.255.0.0" + assert attrs["bitRate"] == 72200000 + assert s.native_value == -68 + assert s.native_unit_of_measurement == SIGNAL_STRENGTH_DECIBELS_MILLIWATT + + assert s.device_class == SensorDeviceClass.SIGNAL_STRENGTH + assert s.state_class == STATE_CLASS_MEASUREMENT + + identifiers = s.device_info["identifiers"] + for x in identifiers: + assert x[0] == LENNOX_DOMAIN + assert x[1] == system.unique_id + + + +@pytest.mark.asyncio +async def test_wifi_rssi_sensor_subscription(hass, manager: Manager): + manager.is_metric = False + system: lennox_system = manager.api.system_list[0] + message = loadfile("wifi_interface_status.json", system.sysId) + system.processMessage(message) + s = WifiRSSISensor(hass, manager, system) + await s.async_added_to_hass() + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_rssi": system.wifi_rssi + 1} + system.attr_updater(update_set, "wifi_rssi") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.native_value == -67 + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_macAddr": "newMac"} + system.attr_updater(update_set, "wifi_macAddr") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["macAddr"] == "newMac" + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_ssid": "newSSID"} + system.attr_updater(update_set, "wifi_ssid") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["ssid"] == "newSSID" + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_ip": "1.1.1.1"} + system.attr_updater(update_set, "wifi_ip") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["ip"] == "1.1.1.1" + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_router": "2.2.2.2"} + system.attr_updater(update_set, "wifi_router") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["router"] == "2.2.2.2" + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_dns": "3.3.3.3"} + system.attr_updater(update_set, "wifi_dns") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["dns"] == "3.3.3.3" + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_dns2": "5.5.5.5"} + system.attr_updater(update_set, "wifi_dns2") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["dns2"] == "5.5.5.5" + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_subnetMask": "255.255.255.0"} + system.attr_updater(update_set, "wifi_subnetMask") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["subnetMask"] == "255.255.255.0" + + with patch.object(s, "schedule_update_ha_state") as update_callback: + update_set = {"wifi_bitRate": 10 } + system.attr_updater(update_set, "wifi_bitRate") + system.executeOnUpdateCallbacks() + assert update_callback.call_count == 1 + assert s.extra_state_attributes["bitRate"] == 10 + + conftest_base_entity_availability(manager, system, s) \ No newline at end of file