Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.2.9 dev #167

Merged
merged 27 commits into from
Jul 2, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
79bd525
Initial work on adding dignostics cleanly upstream
HyperActiveJ May 1, 2022
a9844ba
Bug Fixes
HyperActiveJ May 1, 2022
03922a2
Working Sell
HyperActiveJ May 1, 2022
8be7820
A couple of cleanups
HyperActiveJ May 1, 2022
64bb63a
Cleanup
HyperActiveJ May 1, 2022
28efd9a
Whitespace fix
HyperActiveJ May 1, 2022
159df12
Fix a variety of typos in strings
deviantintegral Jun 14, 2022
4105715
Merge pull request #166 from deviantintegral/patch-2
PeteRager Jun 14, 2022
9a52148
Merge branch '0.2.9-dev' into master
PeteRager Jun 18, 2022
5faac75
Merge pull request #152 from HyperActiveJ/master
PeteRager Jun 18, 2022
e6a6590
Unit tests and updates for diagsensor
PeteRager Jun 21, 2022
b3d5b97
Add configuration for creating diagnostic sensors
PeteRager Jun 21, 2022
65a6c7d
Update strings
PeteRager Jun 21, 2022
72ed4f3
Add binary_sensors for relay and internet status
PeteRager Jun 22, 2022
f7a22b5
API equipment / diagnostic refactoring
PeteRager Jun 23, 2022
89967cb
Diagsensor unavailable if diag level not 1 or 2
PeteRager Jun 25, 2022
91e5c56
Log warning if creating diagnostic sensors when connected to internet
PeteRager Jun 25, 2022
26b626c
#170 - Entities unavailable when API not connected
PeteRager Jun 29, 2022
17032bd
Power Inverter to be unavailable when diag level is 0
PeteRager Jun 29, 2022
cfa33fc
Update diagnostics.md
PeteRager Jun 29, 2022
43b5518
Add diagnostic image
PeteRager Jun 30, 2022
5e342e4
Merge branch '0.2.9-dev' of https://github.com/PeteRager/lennoxs30 in…
PeteRager Jun 30, 2022
78d80b7
Add WARN! for diagnostic sensors
PeteRager Jun 30, 2022
24ae44a
Fixes
PeteRager Jul 1, 2022
8949b51
Compose the name in the constructor
PeteRager Jul 1, 2022
b923dd3
Log warning if setting diagnostis level
PeteRager Jul 1, 2022
254b55c
Updates
PeteRager Jul 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 69 additions & 25 deletions README.MD

Large diffs are not rendered by default.

44 changes: 41 additions & 3 deletions custom_components/lennoxs30/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
CONF_ALLERGEN_DEFENDER_SWITCH,
CONF_APP_ID,
CONF_CREATE_INVERTER_POWER,
CONF_CREATE_DIAGNOSTICS_SENSORS,
CONF_CREATE_SENSORS,
CONF_FAST_POLL_INTERVAL,
CONF_FAST_POLL_COUNT,
Expand Down Expand Up @@ -201,6 +202,9 @@ def _upgrade_config(config: dict, current_version: int) -> int:
else DEFAULT_LOCAL_TIMEOUT
)
current_version = 2
if current_version == 2:
config[CONF_CREATE_DIAGNOSTICS_SENSORS] = False
current_version = 3
return current_version


Expand Down Expand Up @@ -238,17 +242,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
index = 0

is_cloud = entry.data[CONF_CLOUD_CONNECTION]

create_inverter_power: bool = False
conf_protocol: str = None
create_diagnostic_sensors: bool = False

if is_cloud == True:
host_name: str = None
email = entry.data[CONF_EMAIL]
password = entry.data[CONF_PASSWORD]
create_inverter_power: bool = False
conf_protocol: str = None
else:
host_name = entry.data[CONF_HOST]
email: str = None
password: str = None
create_inverter_power: bool = entry.data[CONF_CREATE_INVERTER_POWER]
create_diagnostic_sensors = entry.data[CONF_CREATE_DIAGNOSTICS_SENSORS]
conf_protocol: str = entry.data[CONF_PROTOCOL]

if CONF_APP_ID in entry.data:
Expand All @@ -273,7 +281,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
conf_message_debug_file = None

_LOGGER.debug(
f"async_setup starting scan_interval [{poll_interval}] fast_scan_interval[{fast_poll_interval}] app_id [{app_id}] config_init_wait_time [{conf_init_wait_time}] create_sensors [{create_sensors}] create_inverter_power [{create_inverter_power}] timeout [{timeout}]"
f"async_setup starting scan_interval [{poll_interval}] fast_scan_interval[{fast_poll_interval}] app_id [{app_id}] config_init_wait_time [{conf_init_wait_time}] create_sensors [{create_sensors}] create_inverter_power [{create_inverter_power}] create_diagnostic_sensors [{create_diagnostic_sensors}] timeout [{timeout}]"
)

manager = Manager(
Expand All @@ -296,6 +304,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
pii_message_logs=conf_pii_in_message_logs,
message_debug_logging=conf_message_debug_logging,
message_logging_file=conf_message_debug_file,
create_diagnostic_sensors=create_diagnostic_sensors,
)
try:
listener = hass.bus.async_listen_once(
Expand Down Expand Up @@ -366,6 +375,7 @@ def __init__(
pii_message_logs: bool = False,
message_debug_logging: bool = True,
message_logging_file: str = None,
create_diagnostic_sensors: bool = False,
):
self._config_entry: ConfigEntry = config
self._reinitialize: bool = False
Expand Down Expand Up @@ -398,8 +408,11 @@ def __init__(
self._allergenDefenderSwitch = allergenDefenderSwitch
self._createSensors: bool = create_sensors
self._create_inverter_power: bool = create_inverter_power
self._create_diagnostic_sensors: bool = create_diagnostic_sensors
self._conf_init_wait_time = conf_init_wait_time
self._is_metric: bool = hass.config.units.is_metric
self.connected = False
self._cs_callbacks = []
if index == 0:
self.connection_state = DOMAIN_STATE
else:
Expand All @@ -422,10 +435,35 @@ async def async_shutdown(self, event: Event) -> None:
_LOGGER.debug(f"async_shutdown complete [{self._ip_address}]")

def updateState(self, state: int) -> None:
if state == DS_CONNECTED and self.connected == False:
self.connected = True
self.executeConnectionStateCallbacks()
elif (
state
in (
DS_RETRY_WAIT,
DS_LOGIN_FAILED,
)
and self.connected == True
):
self.connected = False
self.executeConnectionStateCallbacks()
self._hass.states.async_set(
self.connection_state, state, self.getMetricsList(), force_update=True
)

def registerConnectionStateCallback(self, callbackfunc):
self._cs_callbacks.append({"func": callbackfunc})

def executeConnectionStateCallbacks(self):
for callback in self._cs_callbacks:
callbackfunc = callback["func"]
try:
callbackfunc(self.connected)
except Exception as e:
# Log and eat this exception so we can process other callbacks
_LOGGER.exception("executeConnectionStateCallbacks - failed ")

def getMetricsList(self):
list = self._api.metrics.getMetricList()
# TODO these are at the individual S30 level, when we have a device object we should move this there
Expand Down
33 changes: 33 additions & 0 deletions custom_components/lennoxs30/base_entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import logging
from . import Manager

_LOGGER = logging.getLogger(__name__)


class S30BaseEntity(object):
def __init__(self, manager: Manager):
self._manager: Manager = manager

async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
self._manager.registerConnectionStateCallback(self.connection_state_callback)
await super().async_added_to_hass()

def connection_state_callback(self, connected: bool):
_LOGGER.debug(f"connection_state_callback connected [{connected}]")
self.schedule_update_ha_state()

@property
def available(self):
if self._manager.connected == False:
return False
return super().available

def update(self):
"""Update data from the thermostat API."""
return True

@property
def should_poll(self):
"""No polling needed."""
return False
166 changes: 153 additions & 13 deletions custom_components/lennoxs30/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
"""Support for Lennoxs30 outdoor temperature sensor"""
from typing import Any
from .const import MANAGER

from .base_entity import S30BaseEntity
from .const import (
CONF_CLOUD_CONNECTION,
MANAGER,
UNIQUE_ID_SUFFIX_INTENET_STATUS_SENSOR,
UNIQUE_ID_SUFFIX_RELAY_STATUS_SENSOR,
)
from . import Manager
from homeassistant.core import HomeAssistant
import logging
from lennoxs30api import lennox_system
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity import DeviceInfo, EntityCategory

from homeassistant.components.binary_sensor import (
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_CONNECTIVITY,
BinarySensorEntity,
)

Expand All @@ -33,6 +41,12 @@ async def async_setup_entry(
sensor = S30HomeStateBinarySensor(hass, manager, system)
sensor_list.append(sensor)

if entry.data[CONF_CLOUD_CONNECTION] == False:
sensor = S30InternetStatus(hass, manager, system)
sensor_list.append(sensor)
sensor = S30RelayServerStatus(hass, manager, system)
sensor_list.append(sensor)

if len(sensor_list) != 0:
async_add_entities(sensor_list, True)
_LOGGER.debug(
Expand All @@ -46,11 +60,18 @@ async def async_setup_entry(
return False


class S30HomeStateBinarySensor(BinarySensorEntity):
class S30HomeStateBinarySensor(S30BaseEntity, BinarySensorEntity):
def __init__(self, hass: HomeAssistant, manager: Manager, system: lennox_system):
super().__init__(manager)
self._hass = hass
self._manager = manager
self._system = system
self._myname = self._system.name + "_home_state"

async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
_LOGGER.debug(
f"async_added_to_hass S30HomeStateBinarySensor myname [{self._myname}]"
)
self._system.registerOnUpdateCallback(
self.update_callback,
[
Expand All @@ -62,7 +83,7 @@ def __init__(self, hass: HomeAssistant, manager: Manager, system: lennox_system)
"sa_setpointState",
],
)
self._myname = self._system.name + "_home_state"
await super().async_added_to_hass()

def update_callback(self):
_LOGGER.debug(
Expand All @@ -88,22 +109,72 @@ def extra_state_attributes(self):
attrs["smart_away_setpoint_state"] = self._system.sa_setpointState
return attrs

def update(self):
"""Update data from the thermostat API."""
return True
@property
def name(self):
return self._myname

@property
def should_poll(self):
"""No polling needed."""
return False
def is_on(self):
return self._system.get_away_mode() == False

@property
def device_info(self) -> DeviceInfo:
"""Return device info."""
return {
"identifiers": {(DOMAIN, self._system.unique_id())},
}

@property
def device_class(self):
return DEVICE_CLASS_PRESENCE


class S30InternetStatus(S30BaseEntity, BinarySensorEntity):
def __init__(self, hass: HomeAssistant, manager: Manager, system: lennox_system):
super().__init__(manager)
self._hass = hass
self._system = system
self._myname = self._system.name + "_internet_status"

async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
_LOGGER.debug(f"async_added_to_hass S30InternetStatus myname [{self._myname}]")
self._system.registerOnUpdateCallback(
self.update_callback,
[
"internetStatus",
],
)
await super().async_added_to_hass()

def update_callback(self):
_LOGGER.debug(f"update_callback S30InternetStatus myname [{self._myname}]")
self.schedule_update_ha_state()

@property
def unique_id(self) -> str:
# HA fails with dashes in IDs
return (
self._system.unique_id() + UNIQUE_ID_SUFFIX_INTENET_STATUS_SENSOR
).replace("-", "")

@property
def extra_state_attributes(self):
return {}

@property
def available(self):
if self._system.internetStatus == None:
return False
return super().available

@property
def name(self):
return self._myname

@property
def is_on(self):
return self._system.get_away_mode() == False
return self._system.internetStatus == True

@property
def device_info(self) -> DeviceInfo:
Expand All @@ -114,4 +185,73 @@ def device_info(self) -> DeviceInfo:

@property
def device_class(self):
return DEVICE_CLASS_PRESENCE
return DEVICE_CLASS_CONNECTIVITY

@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC


class S30RelayServerStatus(S30BaseEntity, BinarySensorEntity):
def __init__(self, hass: HomeAssistant, manager: Manager, system: lennox_system):
super().__init__(manager)
self._hass = hass
self._system = system
self._myname = self._system.name + "_relay_server"

async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
_LOGGER.debug(
f"async_added_to_hass S30RelayServerStatus myname [{self._myname}]"
)
self._system.registerOnUpdateCallback(
self.update_callback,
[
"relayServerConnected",
],
)
await super().async_added_to_hass()

def update_callback(self):
_LOGGER.debug(f"update_callback S30RelayServerStatus myname [{self._myname}]")
self.schedule_update_ha_state()

@property
def unique_id(self) -> str:
# HA fails with dashes in IDs
return (
self._system.unique_id() + UNIQUE_ID_SUFFIX_RELAY_STATUS_SENSOR
).replace("-", "")

@property
def extra_state_attributes(self):
return {}

@property
def name(self):
return self._myname

@property
def available(self):
if self._system.relayServerConnected == None:
return False
return super().available

@property
def is_on(self):
return self._system.relayServerConnected == True

@property
def device_info(self) -> DeviceInfo:
"""Return device info."""
return {
"identifiers": {(DOMAIN, self._system.unique_id())},
}

@property
def device_class(self):
return DEVICE_CLASS_CONNECTIVITY

@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
Loading