Ihc component and platforms#10916
Conversation
| switch.set_name(name) | ||
| _LOGGER.info("IHC switch set name: " + name + " " + str(ihcid)) | ||
| else: | ||
| switch = IHCSwitch(ihccontroller, name, ihcid, ihcname, ihcnote, ihcposition) |
| return add_switch(devices, ihccontroller, ihcid, name, False, ihcname, ihcnote, ihcposition) | ||
|
|
||
| def add_switch(devices, ihccontroller, ihcid: int, name: str, overwrite: bool = False, | ||
| ihcname: str = "", ihcnote: str = "", ihcposition: str = "") -> IHCSwitch: |
| ihcposition = product.attrib['position'] | ||
| return add_switch(devices, ihccontroller, ihcid, name, False, ihcname, ihcnote, ihcposition) | ||
|
|
||
| def add_switch(devices, ihccontroller, ihcid: int, name: str, overwrite: bool = False, |
There was a problem hiding this comment.
expected 2 blank lines, found 1
line too long (86 > 79 characters)
| ihcname = product.attrib['name'] | ||
| ihcnote = product.attrib['note'] | ||
| ihcposition = product.attrib['position'] | ||
| return add_switch(devices, ihccontroller, ihcid, name, False, ihcname, ihcnote, ihcposition) |
| pass | ||
|
|
||
|
|
||
| def add_switch_from_node(devices, ihccontroller, ihcid: int, name: str, product) -> IHCSwitch: |
|
|
||
| _IHCSWITCHES = {} | ||
|
|
||
| def setup_platform(hass, config, add_devices_callback, discovery_info=None): |
|
|
||
| PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
| vol.Optional(CONF_AUTOSETUP, default='False'): cv.boolean, | ||
| vol.Optional(CONF_SWITCHES) : |
| """ | ||
| IHC switch platform that implements a switch. | ||
| """ | ||
| # pylint: disable=too-many-arguments, too-many-instance-attributes, bare-except, unused-argument, missing-docstring |
There was a problem hiding this comment.
line too long (115 > 79 characters)
|
|
||
| def add_sensor(devices, ihccontroller, ihcid: int, name: str, sensortype: str, | ||
| unit: str, overwrite: bool = False, | ||
| ihcname: str = "", ihcnote: str = "", ihcposition: str = "") -> IHCSensor: |
| except: | ||
| pass | ||
|
|
||
| def add_sensor(devices, ihccontroller, ihcid: int, name: str, sensortype: str, |
| switch.set_name(name) | ||
| _LOGGER.info("IHC switch set name: " + name + " " + str(ihcid)) | ||
| else: | ||
| switch = IHCSwitch(ihccontroller, name, ihcid, ihcname, ihcnote, ihcposition) |
| return add_switch(devices, ihccontroller, ihcid, name, False, ihcname, ihcnote, ihcposition) | ||
|
|
||
| def add_switch(devices, ihccontroller, ihcid: int, name: str, overwrite: bool = False, | ||
| ihcname: str = "", ihcnote: str = "", ihcposition: str = "") -> IHCSwitch: |
| ihcposition = product.attrib['position'] | ||
| return add_switch(devices, ihccontroller, ihcid, name, False, ihcname, ihcnote, ihcposition) | ||
|
|
||
| def add_switch(devices, ihccontroller, ihcid: int, name: str, overwrite: bool = False, |
There was a problem hiding this comment.
expected 2 blank lines, found 1
line too long (86 > 79 characters)
| ihcname = product.attrib['name'] | ||
| ihcnote = product.attrib['note'] | ||
| ihcposition = product.attrib['position'] | ||
| return add_switch(devices, ihccontroller, ihcid, name, False, ihcname, ihcnote, ihcposition) |
| pass | ||
|
|
||
|
|
||
| def add_switch_from_node(devices, ihccontroller, ihcid: int, name: str, product) -> IHCSwitch: |
|
|
||
| _IHCSWITCHES = {} | ||
|
|
||
| def setup_platform(hass, config, add_devices_callback, discovery_info=None): |
|
|
||
| PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
| vol.Optional(CONF_AUTOSETUP, default='False'): cv.boolean, | ||
| vol.Optional(CONF_SWITCHES) : |
| """ | ||
| IHC switch platform that implements a switch. | ||
| """ | ||
| # pylint: disable=too-many-arguments, too-many-instance-attributes, bare-except, unused-argument, missing-docstring |
There was a problem hiding this comment.
line too long (115 > 79 characters)
|
|
||
| def add_sensor(devices, ihccontroller, ihcid: int, name: str, sensortype: str, | ||
| unit: str, overwrite: bool = False, | ||
| ihcname: str = "", ihcnote: str = "", ihcposition: str = "") -> IHCSensor: |
| except: | ||
| pass | ||
|
|
||
| def add_sensor(devices, ihccontroller, ihcid: int, name: str, sensortype: str, |
There was a problem hiding this comment.
Thanks for the PR! I've commented in depth on the binary sensor platform and on the component.
There are quite many comments, but I've tried to be very specific for these two modules. Hopefully my comments make sense so that you can refactor the other modules in a similar way. But let me know if I should clarify anything.
In general I think you can move many functions from the platforms to the component and be able to reuse them in all the platforms by modifying the functions somewhat and then import them from the component to the platforms as needed.
One thing that you could think about is to change the configuration from individual platform config sections, to have a more streamlined config section only for the ihc component and use the discovery helper to set up the platforms from the component. This might not be possible if you have very specific configuration needs for the platforms. But it's something to consider since it could simplify things.
Another thing that you should think about is moving more of the PRODUCTAUTOSETUP to the ihcsdk library. Is it needed to have that info be defined in home assistant, or could it just be imported?
| @@ -0,0 +1,183 @@ | |||
| """IHC binary sensor platform.""" | |||
| # pylint: disable=too-many-instance-attributes | |||
There was a problem hiding this comment.
Remove this. It's globally disabled already.
There was a problem hiding this comment.
Done. (The reason for having these is because I am developing on Windows, and had problems getting pylint behave the same way as on linux.)
| """IHC binary sensor platform.""" | ||
| # pylint: disable=too-many-instance-attributes | ||
| import logging | ||
| import voluptuous as vol |
There was a problem hiding this comment.
Please group and sort imports according to PEP8.
https://www.python.org/dev/peps/pep-0008/#imports
Sort the imports 🔡 within each group standard library, 3rd party and homeassistant.
|
|
||
| PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
| vol.Optional(CONF_AUTOSETUP, default='False'): cv.boolean, | ||
| vol.Optional(CONF_BINARY_SENSORS): |
There was a problem hiding this comment.
Also validate the list with cv.ensure_list, and default to empty list.
vol.Optional(CONF_BINARY_SENSORS, default=[]): vol.All(cv.ensure_list, [...])| }) | ||
|
|
||
|
|
||
| PRODUCTAUTOSETUP = [ |
There was a problem hiding this comment.
The product auto setup has been completely changed and simplified. The configuration has been moved out of the code to a separate configuration file. This also allow the end user to modify the auto configuration (something I have had as a request from some of the users)
| @@ -0,0 +1,9 @@ | |||
| """IHC platform constants.""" | |||
|
|
|||
| CONF_AUTOSETUP = 'autosetup' | |||
There was a problem hiding this comment.
CONF_AUTO_SETUP = 'auto_setup'| """Initialize IHC attributes.""" | ||
| self.ihc = ihccontroller | ||
| self._name = name | ||
| self._ihcid = ihcid |
| class IHCDevice: | ||
| """Base class for all ihc devices.""" | ||
|
|
||
| def __init__(self, ihccontroller, name, ihcid, ihcname: str, |
There was a problem hiding this comment.
Please use snake case variable names when the names contain multiple words for legibility. Eg:
def __init__(self, ihc_controller, name, ihc_id, ihc_name: str,| self._name = name | ||
| self._ihcid = ihcid | ||
| self.ihcname = ihcname | ||
| self.ihcnote = ihcnote |
| self._ihcid = ihcid | ||
| self.ihcname = ihcname | ||
| self.ihcnote = ihcnote | ||
| self.ihcposition = ihcposition |
| _IHCSWITCHES = {} | ||
|
|
||
|
|
||
| def setup_platform(hass, config, add_devices_callback, discovery_info=None): |
|
Most of the changes requested in the review have been fixed, and I just replied "done" to each of them. The product auto setup has been simplified and the configuration moved to a separate yaml file, and the user can overwrite the default configuration by creating a yaml file in the home assistant configuration folder. This was primarily because some users requested to be able top do this. About moving platform configuration to the component, I don't think it will be more simple because you would have to specify what platform each manual setup of a ihc resource should map to. The discovery helper is that not for finding component using uPNP, SSDP? - so if the ihc controller did have this, I could use it to find the ihc controller (unfortunately the ihc controller do not have something like this). I don't understand how I could use this for setup of the platforms for the ihc component. |
MartinHjelmare
left a comment
There was a problem hiding this comment.
Nice refactor! See comments below.
| """Initialize the caching of the project.""" | ||
| self.ihc_controller = ihc_controller | ||
| self.info = False | ||
| project = self.ihc_controller.get_project() |
There was a problem hiding this comment.
Please break this out and do this before instantiating the Ihc wrapper. You don't want to have an instance that is not completely setup. Pass the project to the init method.
| AUTO_SETUP_YAML) | ||
| yaml = load_yaml_config_file(yaml_path) | ||
| try: | ||
| self.auto_setup_conf = AUTO_SETUP_SCHEMA(yaml) |
| return False | ||
|
|
||
| ihc = Ihc(hass, ihc_controller) | ||
| ihc.info = conf.get(CONF_INFO) |
There was a problem hiding this comment.
Why not pass this to the init method of Ihc?
There was a problem hiding this comment.
I have moved the auto setup to the IHC component and the IHC wrapper is no longer needed
| and node.attrib['setting'] == 'yes'): | ||
| continue | ||
| ihc_id = int(node.attrib['id'].strip('_'), 0) | ||
| name = groupname + "_" + str(ihc_id) |
There was a problem hiding this comment.
Please use new style string formatting, not string concatenation.
There was a problem hiding this comment.
I assume "new style" it the string.format (I am new to python so i don't know what is new and old)
|
|
||
| def set_runtime_value_bool(call): | ||
| """Set a IHC runtime bool value service function.""" | ||
| ihc_id = int(call.data.get(ATTR_IHC_ID, 0)) |
There was a problem hiding this comment.
Schema validation has already ensured the type is correct. Please remove the int copy.
There was a problem hiding this comment.
Schema should have the default value. Don't set that here.
| @property | ||
| def today_energy_kwh(self): | ||
| """Return the today total energy usage in kWh.""" | ||
| return 0 |
|
|
||
| def turn_on(self, **kwargs): | ||
| """Turn the switch on.""" | ||
| self._state = True |
| """Turn the switch on.""" | ||
| self._state = True | ||
| self.ihc.ihc_controller.set_runtime_value_bool(self._ihc_id, True) | ||
| self.schedule_update_ha_state() |
|
|
||
| def turn_off(self, **kwargs): | ||
| """Turn the device off.""" | ||
| self._state = False |
| self.inverting = inverting | ||
|
|
||
| @property | ||
| def should_poll(self): |
There was a problem hiding this comment.
You can move this to the main device class.
| """Turn the device off.""" | ||
| self._state = False | ||
| self.ihc.ihc_controller.set_runtime_value_bool(self._ihc_id, False) | ||
| self.schedule_update_ha_state() |
|
I have moved the auto setup from each platform to the IHC component - as you also suggested. (I looked at how other components are doing this, and found the dicovery_info to be just a dictionary of whatever data you want to pass toeach platform) This simplifies it even more and the IHC wrapper is also no longer needed, because all the auto setup is within the ihc component. If the user has no manual IHC products to setup, only the IHC component need to be setup - also makes it more simple to the user. |
|
@dingusdk i've looked a bit at this and i could not find implementation regarding the Wireless IHC components such as IHC Battery operated switches like https://github.com/home-assistant/home-assistant/pull/10321/files#diff-80c939d2fd205960d0558ad5cfdcf55bR192 or https://www.harald-nyborg.dk/p8943/wireless-batteritryk-4-slutte. I would love to have support for those, if you decide to do something like that then i strongly suggest you don't create entities for these and just send events on the HASS buss since such switches does not have a state, see here how to send an event https://github.com/home-assistant/home-assistant/pull/10321/files#diff-80c939d2fd205960d0558ad5cfdcf55bR192 Also if you add support for these devices AND the IHC api has information on Battery status then the battery status should be created in HASS as a separete entity for each switch. |
MartinHjelmare
left a comment
There was a problem hiding this comment.
Looks very nice! Mostly some minor touch-ups below.
NB, the lazy loading of service descriptions that affect your registration of services.
|
|
||
|
|
||
| def setup_platform(hass, config, add_devices, discovery_info=None): | ||
| """Set up the IHC binary setsor platform.""" |
| vol.Required(CONF_NODE): cv.string, | ||
| vol.Optional(CONF_TYPE, default='Temperature'): cv.string, | ||
| vol.Optional(CONF_UNIT_OF_MEASUREMENT, | ||
| default='°C'): cv.string, |
There was a problem hiding this comment.
Import TEMP_CELSIUS from const.py and use that as default unit.
| yaml = load_yaml_config_file(yaml_path) | ||
| try: | ||
| auto_setup_conf = AUTO_SETUP_SCHEMA(yaml) | ||
| except VoluptuousError as exception: |
There was a problem hiding this comment.
except vol.Invalid as exc:There was a problem hiding this comment.
The except VoluptuousError will catch all error from voluptuous (I copied it from somewhere else in home-assistant), but I assume your reason is that vol.Invalid is better because the schema is always valid (build into the code). I have changed it to vol.Invalid
| component_setup = auto_setup_conf[component] | ||
| discovery_info = get_discovery_info(component_setup, groups) | ||
| if len(discovery_info) > 0: | ||
| discovery.load_platform(hass, component, DOMAIN, discovery_info) |
There was a problem hiding this comment.
Add the original config as fifth argument.
There was a problem hiding this comment.
I have added the config. It is not used, and I can see other components also not passing the config, but I guess it is something like good practice in case it is need later.
| for component in IHC_PLATFORMS: | ||
| component_setup = auto_setup_conf[component] | ||
| discovery_info = get_discovery_info(component_setup, groups) | ||
| if len(discovery_info) > 0: |
| @asyncio.coroutine | ||
| def async_added_to_hass(self): | ||
| """Add callback for ihc changes.""" | ||
| self.info = self.hass.data[IHC_DATA][IHC_INFO] |
There was a problem hiding this comment.
This you should be able to pass in __init__ via setup_platform.
| def on_ihc_change(self, ihc_id, value): | ||
| """Callback when ihc resource changes. | ||
|
|
||
| Derived classes must overwrite this todo device specific stuff. |
| description: The boolean value to set | ||
|
|
||
| set_runtime_value_int: | ||
| description: Set a integer runtime value on the ihc controller |
|
|
||
| def on_ihc_change(self, ihc_id, value): | ||
| """Callback from Ihc notifications.""" | ||
| if type(value) is int: |
There was a problem hiding this comment.
if isinstance(value, int):There was a problem hiding this comment.
if isinstance(value, int):is not working because if value is bool it will also return true. I have reversed to
if isinstance(value, bool):Then it is working because - when value is int it will return false. (Now I can remove the #pylint disable)
| from homeassistant.components.ihc import ( | ||
| validate_name, IHC_DATA, IHC_CONTROLLER) | ||
| from homeassistant.components.ihc.ihcdevice import IHCDevice | ||
| from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) |
There was a problem hiding this comment.
Imported names that fit a single line don't need parenthesis.
|
@donnib You are right about the IHC wireless switches, and it would be nice to have push buttons in home assistant. If you want to have the events from a IHC button into home assistant you can associate the IHC button resource id with a binary sensor in home assistant (manually setup). The binary sensor will be on when the button is pressed and off when not pressed - then you can use it from automation to control something. I don't think IHC expose the battery level anywhere in the soap api - at least I have not been able to find it. It is only in the IHC log - as a warning when battery level is low. |
Description:
Integration of the LK IHC controller (sold under other names like "Elko Living System" in Sweeden and Norway)
Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#4129>