Issue/add template fans#12027
Conversation
This reverts commit 1e05371.
…ssue/add-template-fans
|
Nice work. Can you add documentation for it as well? |
|
Create PR for adding document here: home-assistant/home-assistant.io#4551 |
|
Could someone review this? |
|
I am using the previous version of this component and it works perfectly, so I assume this one does too. Thanks a lot for resubmitting this and I hope it gets merged soon. |
|
Just a couple small changes and I'll merge this, thanks for adding it. |
| CONF_OSCILLATING_TEMPLATE = 'oscillating_template' | ||
| CONF_ON_ACTION = 'turn_on' | ||
| CONF_OFF_ACTION = 'turn_off' | ||
| CONF_SET_SPEED_ACTION = 'set_speed' |
There was a problem hiding this comment.
I don't think you should add _ACTION here, just CONF_SET_SPEED, etc.
There was a problem hiding this comment.
I am using same naming convention as in light/template so prefer to keep them if u don't might
| CONF_SPEED_LIST = 'speeds' | ||
| CONF_SPEED_TEMPLATE = 'speed_template' | ||
| CONF_OSCILLATING_TEMPLATE = 'oscillating_template' | ||
| CONF_ON_ACTION = 'turn_on' |
There was a problem hiding this comment.
You should use the consts COMMAND_ON and COMMAND_OFF to match the other templates.
There was a problem hiding this comment.
light/template is also using same naming convention and some people might already use my PR so I prefer not to change this.
|
|
||
| from homeassistant.core import callback | ||
| from homeassistant import setup | ||
| import homeassistant.components as core |
There was a problem hiding this comment.
Don't use core here, use component
|
Sorry, I hadn't committed my comments so they weren't showing up for you. I think this is fine to merge if you can just clean up those couple things. Nice job adding all the tests up front. |
|
@balloob any objections to merging this? |
|
Was using this as a custom component until merged, but it no longer works with the voluptuous changes for 0.64 just to let you know |
| _VALID_STATES = [STATE_ON, STATE_OFF, 'true', 'false'] | ||
|
|
||
| FAN_SCHEMA = vol.Schema({ | ||
| vol.Optional(CONF_FRIENDLY_NAME, default=None): cv.string, |
There was a problem hiding this comment.
Remove default=None from your schema. It's not allowed to have a default that is not a valid value.
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_setup_platform(hass, config, async_add_devices, discovery_info=None): |
There was a problem hiding this comment.
We no longer use @asyncio.coroutine, easy to fix by removing the decorator and instead add async in front of func name: async def async_setup_platform(…. All yield from will have to be replaced with await
| template_entity_ids = set() | ||
|
|
||
| temp_ids = state_template.extract_entities() | ||
| if str(temp_ids) != MATCH_ALL: |
There was a problem hiding this comment.
Why stringify? you can compare a list with a string.
Also, if it is match_all, shouldn't you make sure that you match all entity ids? Now if 1 is match all but other 2 are not, you will not match all.
There was a problem hiding this comment.
Honestly, I copied it from light/template so I don't understand it 100%. It is working as light template so I think its good enough to be merged.
There was a problem hiding this comment.
Please remove the call to str() and compare temp_ids directly to MATCH_ALL.
| ) | ||
|
|
||
| async_add_devices(fans) | ||
| return True |
There was a problem hiding this comment.
Remove this. Platforms don't return a value.
|
@balloob , I updated the code as your comments (except the "stringify" one). Please review again. |
|
|
||
| if speed_template: | ||
| temp_ids = speed_template.extract_entities() | ||
| if str(temp_ids) != MATCH_ALL: |
| template_entity_ids |= set(temp_ids) | ||
|
|
||
| if oscillating_template: | ||
| temp_ids = oscillating_template.extract_entities() |
There was a problem hiding this comment.
if one is MATCH_ALL, they should all be MATCH_ALL. This is not currently the case. If the oscillating_template returns MATCH_ALL, it will just use extracted entities of the other templates.
|
|
||
| if speed_template: | ||
| temp_ids = speed_template.extract_entities() | ||
| if temp_ids != MATCH_ALL: |
There was a problem hiding this comment.
This still won't work. The moment you get a MATCH_ALL, you should just match on MATCH_ALL.
There was a problem hiding this comment.
Sorry but I dont get it, could u give the correct code.
Thanks
There was a problem hiding this comment.
You could take a look at how I recently did it in the template sensor platform:
|
|
||
| if speed_template: | ||
| temp_ids = speed_template.extract_entities() | ||
| if temp_ids != MATCH_ALL: |
There was a problem hiding this comment.
You could take a look at how I recently did it in the template sensor platform:
|
|
||
| This method is a coroutine. | ||
| """ | ||
| self._state = False |
There was a problem hiding this comment.
Shouldn't we only set the state to False once the off script terminates? + If the off script raises an exception I would expect the state to remain in the previous state.
| self.async_schedule_update_ha_state(True) | ||
|
|
||
| self.hass.bus.async_listen_once( | ||
| EVENT_HOMEASSISTANT_START, template_fan_startup) |
There was a problem hiding this comment.
I don't understand why we should only call this on EVENT_HOMEASSISTANT_START. This code would break once we start dynamically loading/unloading entities. Maybe have a look at how it's done in the light group platform:
There was a problem hiding this comment.
I am doing the same as in light/template and sensor/template. Using the code in light group did not work. Fan is no changed when other entities changed
|
|
||
| async def async_update(self): | ||
| """Update the state from the template.""" | ||
| _LOGGER.info('Updating fan %s', self._name) |
There was a problem hiding this comment.
This seems like quite the unnecessary logging statement. Even more it's info and not debug.
| self._state = None | ||
|
|
||
| # Validate state | ||
| if state in _VALID_STATES: |
There was a problem hiding this comment.
I think if the previous call produces a TemplateError, state will not be set, resulting in NameError: name 'state' is not defined.
| self._state = None | ||
|
|
||
| # Update speed if 'speed_template' is configured | ||
| if self._speed_template: |
There was a problem hiding this comment.
PEP8 recommend using is not None:
if self._speed_template is not None:| {ATTR_OSCILLATING: oscillating} | ||
| ) | ||
| else: | ||
| _LOGGER.error( |
There was a problem hiding this comment.
This check is already handled by the service schema:
Also if it were needed, it should definitely not go into this platform, but rather in the core fan definition.
| async def async_turn_on(self, speed: str = None) -> None: | ||
| """Turn on the fan. | ||
|
|
||
| This method is a coroutine. |
There was a problem hiding this comment.
I think by now (especially with async def) we don't need to plaster "This method is a coroutine." everywhere where it's abundantly clear from the context.
| } | ||
| }) | ||
|
|
||
| self.hass.start() |
There was a problem hiding this comment.
No need to call self.hass.start() in any of these test cases.
There was a problem hiding this comment.
removing them failed the tests.
|
|
||
| with assert_setup_component(2, 'input_select'): | ||
| assert setup.setup_component(self.hass, 'input_select', { | ||
| 'input_select': { |
There was a problem hiding this comment.
I mean lots of tests do this, but I don't think tests from one platform should depend on another integration, it's just bad testing (and makes changing those other integrations a pain).
Why not just use the state machine manually?
There was a problem hiding this comment.
I want them to be integration tests so we can sure that our scripts run successfully with real results. Please advise an example of state machine way of testing.
|
Would be really great to have this one merged! I'm happy to create a new pull request covering the requested changes. |
|
@OttoWinter could you please review. I updated the code as your comments.
|
|
Looks good 🐬 |
|
Fantastic! |
Description:
Add template fan
Example entry for
configuration.yaml:Checklist:
If user exposed functionality or configuration variables are added/changed:
If the code does not interact with devices:
toxrun successfully. Your PR cannot be merged unless tests pass