Qwikswitch refactor & sensor#13509
Conversation
| "Configure Qwikswitch Light component failed") | ||
| return False | ||
| qsusb = hass.data[QWIKSWITCH] | ||
| devs = [QSLight(id, qsusb) for id in discovery_info[QWIKSWITCH]] |
There was a problem hiding this comment.
Please use another variable name than id, which is a builtin.
| def setup_platform(hass, config, add_devices, discovery_info=None): | ||
| async def async_setup_platform(hass, _, add_devices, discovery_info=None): | ||
| """Add lights from the main Qwikswitch component.""" | ||
| if discovery_info is None: |
There was a problem hiding this comment.
This should be kept, I think. You don't want to continue unless setup is via discovery, right? Keep it and return if true.
There was a problem hiding this comment.
Can do, but should we not raise some exception if a user incorrectly configures one of these platforms? Or log the error then? I dont like failing silently.
There was a problem hiding this comment.
I think documentation is enough in this case. It's our standard procedure at the moment.
| add_devices(devs) | ||
|
|
||
| for _id, dev in zip(discovery_info[QWIKSWITCH], devs): | ||
| hass.helpers.dispatcher.async_dispatcher_connect( |
There was a problem hiding this comment.
Move this to the entity coroutine method async_added_to_hass, ie connect each entity from within itself. Reason is that add_devices schedules a call to add devices, so add_devices is not guaranteed to have added the entities when it returns.
| _LOGGER.info("Waiting for long poll to QSUSB to time out") | ||
| # Discover all devices in QSUSB | ||
| if not await qsusb.update_from_devices(): | ||
| raise PlatformNotReady |
There was a problem hiding this comment.
This only has an effect if raised within setup_platform.
There was a problem hiding this comment.
Unfortunate, for now I will just remove and leave more clever recovery mechnism for a future PR
| vol.Coerce(str), | ||
| vol.Optional(CONF_DIMMER_ADJUST, default=1): CV_DIM_VALUE, | ||
| vol.Optional(CONF_BUTTON_EVENTS): vol.Coerce(str) | ||
| vol.Optional(CONF_BUTTON_EVENTS): cv.ensure_list_csv, |
There was a problem hiding this comment.
ensure_list_csv should only be used to avoid breaking changes. It's better to use plain ensure_list if possible.
There was a problem hiding this comment.
Does it work to add a default empty list here, so you don't need to add that in async_setup?
There was a problem hiding this comment.
It will work, only reason I dont like it is that validating then adds this empty key to the config (which really is not used by many people).
ensure_list_csv is indeed for backward compatibility. It used to be a text deliminated string
| class QSSwitch(QSToggleEntity, SwitchDevice): | ||
| """Switch based on a Qwikswitch relay module.""" | ||
|
|
||
| pass |
| add_devices(devs) | ||
|
|
||
| for _id, dev in zip(discovery_info[QWIKSWITCH], devs): | ||
| hass.helpers.dispatcher.async_dispatcher_connect( |
There was a problem hiding this comment.
Move this to async_added_to_hass.
| def setup_platform(hass, config, add_devices, discovery_info=None): | ||
| async def async_setup_platform(hass, _, add_devices, discovery_info=None): | ||
| """Add switches from the main Qwikswitch component.""" | ||
| if discovery_info is None: |
|
|
||
| async def async_setup_platform(hass, _, add_devices, discovery_info=None): | ||
| """Add lights from the main Qwikswitch component.""" | ||
| qsusb = hass.data[QWIKSWITCH] |
There was a problem hiding this comment.
Return if no discovery_info is passed.
| item[QS_ID], item) | ||
|
|
||
| # Update all ha_objects | ||
| hass.async_add_job(qsusb.update_from_devices) |
There was a problem hiding this comment.
What does this do? Since we're in a callback that is called by qsusb, shouldn't qsusb already know to update its devices?
There was a problem hiding this comment.
The QSUSB API has two main methods, listen and devices. These really have no relationship betwen them and I simply use listen to see when there is activity and call update_from_devices.
If I have access to the event loop in listen I could potentially add it there, but then the API becomes less felxible (although only HA uses it at the moment)
There was a problem hiding this comment.
The aiohttp session is already passed, so passing the event loop too should work from my point of view. But I don't know this library, so you should decide what fits best.
There was a problem hiding this comment.
Lets keep it like this for now, it clearly shows what it does with the API and the API is a very close to match to the manufacturer's API
|
Hi there File "/config/custom_components/qwikswitch.py", line 94, in async_setup |
|
@ipodmusicman I suspect it is not pulling the new library, please delete the two pyqwikswitch folders in HA deletes these during upgrades, but you are not upgrading now and it assumes it already has the correct dependency |
|
@MartinHjelmare thank you for the review, the |
| def async_added_to_hass(self): | ||
| """Listen for updates from QSUSb via dispatcher.""" | ||
| # hass and schedule_update_ha_state is part of Entity/ToggleEntity | ||
| # pylint: disable=no-member |
There was a problem hiding this comment.
Have this class inherit from Entity and remove this disable.
| def supported_features(self): | ||
| """Flag supported features.""" | ||
| return SUPPORT_BRIGHTNESS if self._dim else None | ||
| def async_added_to_hass(self): |
There was a problem hiding this comment.
async def async_added_to_hass(self):
| # hass and schedule_update_ha_state is part of Entity/ToggleEntity | ||
| # pylint: disable=no-member | ||
| self.hass.helpers.dispatcher.async_dispatcher_connect( | ||
| self.qsid, lambda _=None: self.schedule_update_ha_state) |
There was a problem hiding this comment.
You don't need the lambda.
Use async_schedule_update_ha_state instead.
There was a problem hiding this comment.
Ok for async_schedule_update, but I prefer to have the lambda,
There are two async_dispatcher_send calls, one includes a packet as parameter and one not. It should be safe that no switch or light entities will get called with the packet parameter, but I might not have 100% control over this (One scenario is where the devices list removes a device)
There was a problem hiding this comment.
I don't understand the scenario where this problem would surface. If you want to keep it super safe, please define a regular method that accepts *args and calls self.async_schedule_update_ha_state.
|
kellerza, I removed the packages and downloaded the latest code based on your changes now. Not sure if that was the right thing to do based on the below error in the log. |
|
@ipodmusicman you will have to add all 4 qwikswitch files to the |
|
kellerza, I grabbed all 4 files from the beginning, updating them as you pushed changes during the day. I also deleted the site-packages just in case before restart. With the component in place, my one light isn't appearing in HA any more and when I remove the custom_components and restart, my light is back. With the sensor in place, I get the following: I removed the sensor from the config and got the following below: Also to note, that with the sensor in place, even with the errors were occurring in the log, I saw the badge icon at the top and on clicking on it saw the various fields and of course the data block change accordingly as a opened and closed the various channels on the imod. However, it seems to go stale after a bit since I'd leave it for about 10 min and open / close again and no updates reflect. Does one generally display sensors as badges or in cards by default? Would I be able to assign a friendly name to the sensor within the qwikswitch config or would I need to do a customization? Is it possible to create separate sensors per channel? A door sensor or movement sensor only has "1 channel", but an imod can have up to 5 channels. A suggestion on the conifg - you'll need to guide on what is best practice as this is only a suggestion: Let me know what you think? Btw, I got a response from QwikSwitch yesterday. The first byte of the data block indicates the sensor type: 4e = imod I've asked for a comprehensive list including the motion sensor and any other sensors they have like temperature and humidity sensors. I've also asked for example messages if they have for the temp and humidity sensors. The second byte is the firmware version for an imod = third and forth byte indicates which channels are open/closed as well as which channel was affected in a status change - see our discussion om MyBB for a door sensor = 00 = Open, 64 (100 decimal) = Closed |
|
@ipodmusicman (I added three backticks ``` to your comment for code blocks) From the errors, unfortunately we cannot do custom_components, since So you either need to patch qwikswitch in to The second error seems to show that your don't have the latest library again...? Ideally we should merge this and you can then use BTW, see the documentation link for initial config today (incl channel), your might be cleaner |
MartinHjelmare
left a comment
There was a problem hiding this comment.
I'm happy if you do the change below.
| async def async_added_to_hass(self): | ||
| """Listen for updates from QSUSb via dispatcher.""" | ||
| self.hass.helpers.dispatcher.async_dispatcher_connect( | ||
| self.qsid, lambda _=None: self.async_schedule_update_ha_state()) |
There was a problem hiding this comment.
Please swap this lambda for a regular helper method that calls async_schedule_update_ha_state and connect that helper method here.
MartinHjelmare
left a comment
There was a problem hiding this comment.
Nice! Can be merged when build passes.
|
kellerza, Now that this merge is done, what is the best way for us to continue our ongoing conversations? I installed HA on Ubuntu so that I can give it a test drive and get to know the platform so I'd imagine that since it is deviating from the recommended set up on a Pi, things seem to be getting a bit mixed up. :) I plan to purchase a Pi-3b within the next month or so so that I can set things up properly. I saw the channel addition to the documentation. Are you going to consider my config suggestion for the sensors? I'll see how I can expand my skills to Python as I come from a Java background to assist in putting something in place that can be used to interpret the data block in the method below based on the sensor type as well as the bytes themselves so that the method either returns open or closed. For a door sensor (and probably a motion sensor as well), it should be as easy as returning either open or closed based on the value of the third byte, but for the imod, it is different as one would need to know which channel the sensor is configured for to determine which bit to use to decide whether it is open or closed. |
|
If you want to suggest an enhancement please open a feature request in the Feature Requests section of our community forum. If you want to discuss things before opening a PR you can make an RFC issue. Thanks! |
Description:
Refactor of qwikswicth (async/await/dispatchers/feedback from #12641) and adding first version of sensor.
As discussed on MyBB wireframe sensor support for QwikSwitch. Sensors need to implement their own logic over time in the
qsSensorclass incomponents/sensors/qwikswitch/.binary_sensorcan be added later, but lets see how this general sensor starts....Config changes:
sensors:added. Format is dictionary of {endtity_id: qs_id}, so adding a door sensorcmd_buttons:now adds to the defaults and no need to include defaults anymore (backward compatible). Can be a list or csv listRelated issue (if applicable): fixes #
Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#5045
Checklist:
lazytox.If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
REQUIREMENTSvariable ([example][ex-requir]).requirements_all.txtby runningscript/gen_requirements_all.py..coveragerc.