Converted SABnzbd to a component#12915
Conversation
There was a problem hiding this comment.
Mark this as @callback if it's an async method or call schedule_update_ha_state instead of the async version.
There was a problem hiding this comment.
No I/O, just a dict lookup
There was a problem hiding this comment.
Use None as default value to represent unknown state.
There was a problem hiding this comment.
Config entries is the new shizzle, configurator is old. You don't have to upgrade it in this PR, would be nice for a future PR ;)
There was a problem hiding this comment.
Will do. When I started this config entries wasn't a thing yet, but I'll definitely convert this in the future.
There was a problem hiding this comment.
If you don't expect data, pass in vol.Schema({})
There was a problem hiding this comment.
Will do. One question though just for my understanding: why does async_register() have a default value of schema=None? Would it make more sense for the default to be schema=vol.Schema({}) or to not have a default at all? Does schema=None have a special meaning?
There was a problem hiding this comment.
You are inside an async context as you call this from async_setup_platform, this needs to be async_notify_errors
There was a problem hiding this comment.
If this is supposed to be called from an async context, prefix the name with async_
There was a problem hiding this comment.
You cannot do I/O inside an async context 😮
Yield to the executor: conf = await hass.async_add_job(load_json, hass.config.path(CONFIG_FILE)
Same goes for writing.
There was a problem hiding this comment.
You removed the call to hass.async_add_job from the previous code, and so you're doing I/O inside the event loop 😞
|
These should (hopefully) all be fixed now. I do think I may have screwed up your review a little bit by force pushing the updates. I added a commit a while back by fixing a merge conflict directly in the github ui and forgot I didn't have it locally. Then after rebasing upstream and adding commits I ended up with a bunch of conflicts so I just force pushed it. I didn't think it would be an issue since that one commit only touched |
There was a problem hiding this comment.
I don't see any reason to make this configurable. Let's pick a sane default and that's it.
There was a problem hiding this comment.
@balloob do you think 30 seconds is a good default? I made it configurable because, for me, SABnzbd is idle probably 90% of the time and I would be happy only getting updates every 10 minutes or so. However, I can imagine some of the more "power users" would rather have more up-to-date download statistics for use in their automations to, for instance, determine when to reduce download speed or pause downloading altogether to prevent exceeding data caps. With the availability of gigabit+ residential connections, a 30 second update interval could mean downloading 3-4 GB between updates.
There was a problem hiding this comment.
This method is synchronous, so you cannot call an async method.
There was a problem hiding this comment.
Move this to async_added_to_hass. Calling the state update before the entity has been added to home assistant will error.
There was a problem hiding this comment.
Don't pass in hass. It will be set on the entity when it has been added to home assistant.
|
@balloob @MartinHjelmare I am ready to push changes, but my build is going to fail due to #14281. Should I push as-is so I don't screw up the review, or go ahead and rebase first? |
|
It's ok to rebase. Try to avoid squashing the commits though. Then review will not be affected. |
|
Thanks @MartinHjelmare. This should be cleaned up now. I think I finally have my head around the async stuff |
| conf = await hass.async_add_job(load_json, | ||
| hass.config.path(CONFIG_FILE)) | ||
|
|
||
| if conf.get(base_url, {}).get(CONF_API_KEY): |
There was a problem hiding this comment.
api_key = conf.get(base_url, {}).get(CONF_API_KEY, '')| """Handle configuration changes.""" | ||
| api_key = data.get(CONF_API_KEY) | ||
| sab_api = SabnzbdApi(host, api_key) | ||
| if await async_check_sabnzbd(sab_api): |
There was a problem hiding this comment.
Invert this check and return if it's true, to make it a guard clause. Then you can outdent the following code.
| elif service.service == SERVICE_RESUME: | ||
| await sab_api_data.async_resume_queue() | ||
| elif service.service == SERVICE_SET_SPEED: | ||
| speed = service.data.get(ATTR_SPEED, None) |
There was a problem hiding this comment.
None is the default value returned if the key is missing in the dict.
| elif service.service == SERVICE_SET_SPEED: | ||
| speed = service.data.get(ATTR_SPEED, None) | ||
| if speed is None: | ||
| speed = '100' |
There was a problem hiding this comment.
Move this default value to the service schema.
There was a problem hiding this comment.
Use None as default value to represent unknown state.
| self._state = self._sabnzbd_api.get_queue_field(self._field_name) | ||
|
|
||
| if self._type == 'speed': | ||
| self._state = round(float(self._state) / 1024, 1) |
There was a problem hiding this comment.
This will error if state is not a number.
There was a problem hiding this comment.
@MartinHjelmare I can add error handling here to be safe, but this should always be a number. This method will only get called by the dispatcher signal after a successful update and the library will always populate these numeric fields with a numeric value. They will always either be a number from the SABnzbd API or get converted to a numeric value by the pysabnzbd library.
There was a problem hiding this comment.
Ok, fine to keep it as is.
MartinHjelmare
left a comment
There was a problem hiding this comment.
Final comment. Otherwise looks good!
| async def async_setup_platform(hass, config, async_add_devices, | ||
| discovery_info=None): | ||
| """Set up the SABnzbd sensors.""" | ||
| sab_api_data = hass.data[DATA_SABNZBD] |
There was a problem hiding this comment.
We only set up the sensor platform from the component now, correct? So otherwise we don't want to do anything here. Add a guard clause that checks if discovery_info is None and returns if so.
if discovery_info is None:
return
...There was a problem hiding this comment.
Yep, that's correct. Fixed
* Converted SABnzbd to a component * fixed async issues * Made sabnzbd scan interval static. More async fixes. * Sabnzbd component code cleanup * Skip sensor platform setup if discovery_info is None
Description:
Converted SABnzbd sensor platform to a component with services that can be called to interact with the SABnzbd queue. This is a breaking change.
Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#4836
Example entry for
configuration.yaml(if applicable):Checklist:
If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
toxrun successfully. Your PR cannot be merged unless tests passREQUIREMENTSvariable (example).requirements_all.txtby runningscript/gen_requirements_all.py..coveragerc.