Skip to content

zha: Support switches/buttons/remotes#12528

Merged
rcloran merged 1 commit intohome-assistant:devfrom
rcloran:zha-button
Apr 30, 2018
Merged

zha: Support switches/buttons/remotes#12528
rcloran merged 1 commit intohome-assistant:devfrom
rcloran:zha-button

Conversation

@rcloran
Copy link
Copy Markdown
Contributor

@rcloran rcloran commented Feb 19, 2018

Description:

Tested to work with:

  • Philips Hue dimmer switch
  • OSRAM/SYLVANIA Lightify Wireless Dimmer Switch
  • Xiaomi Smart Wireless Switch (button)
  • Iris Care Pendant

Tested and does not work with:

  • Lutron Connected Bulb Remote (reason unknown)
  • IKEA Trådfri Dimmer (reports invalid device type)
  • IKEA Trådfri Remote (invalid device type, still no reports after hacking around that)

Related issue (if applicable): fixes #

Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.github.io#<home-assistant.github.io PR number goes here>

Example entry for configuration.yaml (if applicable):

Checklist:

  • The code change is tested and works locally.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • Local tests with tox run successfully. Your PR cannot be merged unless tests pass
  • New dependencies have been added to the REQUIREMENTS variable (example).
  • New dependencies are only imported inside functions that use them (example).
  • New dependencies have been added to requirements_all.txt by running script/gen_requirements_all.py.
  • New files were added to .coveragerc.

If the code does not interact with devices:

  • Local tests with tox run successfully. Your PR cannot be merged unless tests pass
  • Tests have been added to verify that the new code works.

Comment thread homeassistant/components/zha/const.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace before ':'

Comment thread homeassistant/components/zha/const.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace before ':'

Comment thread homeassistant/components/zha/const.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace before ':'

Comment thread homeassistant/components/zha/const.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace before ':'

Comment thread homeassistant/components/zha/const.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace before ':'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (81 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (84 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local variable 'v' is assigned to but never used

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (87 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (88 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined name 'cluster_type'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (105 > 79 characters)

Comment thread homeassistant/components/zha/const.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined name 'SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS'

Comment thread homeassistant/components/zha/const.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined name 'SINGLE_INPUT_CLUSTER_DEVICE_CLASS'

@rcloran
Copy link
Copy Markdown
Contributor Author

rcloran commented Feb 19, 2018

@igorbernstein2 : Does this work with your GE switches/dimmers?

@Kane610
Copy link
Copy Markdown
Member

Kane610 commented Feb 19, 2018

You should look at exposing remotes button presses as events since they are state less.

@rcloran
Copy link
Copy Markdown
Contributor Author

rcloran commented Feb 20, 2018

I'm happy to work this to just generate events instead of create a binary_sensor, if there's some consensus that'd be better. I'm not sure how other components in hass deal with devices like this, so happy to take pointers.

binary_sensor (or sensor, I guess) has the advantage of having some common bits already wired together, but the disadvantage of not supporting "on, on" type event streams.

@Kane610
Copy link
Copy Markdown
Member

Kane610 commented Feb 20, 2018

I had this discussion when integrating the deConz component. There are some arguments in the pr #10321

@igorbernstein2
Copy link
Copy Markdown
Contributor

igorbernstein2 commented Feb 24, 2018

Doesnt seem to work with my GE ZigBee Lighting Switch 45856GE.
With this config:

homeassistant:
  name: Home
  unit_system: imperial

zha:
  usb_path: /dev/ttyZigbee
  database_path: /home/pi/.config/bellows/app.db

http:
frontend:
config:

The switch is detected on startup:

...
2018-02-24 11:16:19 INFO (MainThread) [homeassistant.setup] Setting up zha
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=zha, service=permit>
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=zha, service=remove>
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.setup] Setup of domain zha took 3.2 seconds.
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.core] Bus:Handling <Event component_loaded[L]: component=zha>
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.loader] Loaded light from homeassistant.components.light
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.loader] Loaded group from homeassistant.components.group
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.loader] Loaded switch from homeassistant.components.switch
...
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.setup] Setting up light
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.setup] Setting up switch
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=switch, service=turn_off>
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=switch, service=turn_on>
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=switch, service=toggle>
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.setup] Setup of domain switch took 0.0 seconds.
2018-02-24 11:16:22 INFO (MainThread) [homeassistant.core] Bus:Handling <Event component_loaded[L]: component=switch>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event platform_discovered[L]: platform=zha, service=load_platform.switch, discovered=discovery_key=00:22:a3:00:00:16:c5:7d-1-6>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=light, service=turn_on>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=light, service=turn_off>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_registered[L]: domain=light, service=toggle>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.setup] Setup of domain light took 0.1 seconds.
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event component_loaded[L]: component=light>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event platform_discovered[L]: platform=zha, service=load_platform.light, discovered=discovery_key=00:22:a3:00:00:1f:37:68-1>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.loader] Loaded switch.zha from homeassistant.components.switch.zha
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.loader] Loaded binary_sensor from homeassistant.components.binary_sensor
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.setup] Setting up binary_sensor
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.setup] Setup of domain binary_sensor took 0.0 seconds.
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event component_loaded[L]: component=binary_sensor>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.loader] Loaded light.zha from homeassistant.components.light.zha
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event platform_discovered[L]: platform=zha, service=load_platform.binary_sensor, discovered=discovery_key=00:22:a3:00:00:1f:37:68-2>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.components.switch] Setting up switch.zha
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.loader] Loaded binary_sensor.zha from homeassistant.components.binary_sensor.zha
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.components.light] Setting up light.zha
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.components.binary_sensor] Setting up binary_sensor.zha
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: entity_id=switch.king_of_fans__inc_hbuniversalcfremote_0016c57d_1_6, new_state=<state switch.king_of_fans__inc_hbuniversalcfremote_0016c57d_1_6=off; friendly_name=King Of Fans,  Inc. HBUniversalCFRemote @ 2018-02-24T11:16:23.421135-05:00>, old_state=None>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: entity_id=binary_sensor.jasco_products_45856_001f3768_2, new_state=<state binary_sensor.jasco_products_45856_001f3768_2=on; level=255 @ 2018-02-24T11:16:23.440202-05:00>, old_state=None>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: service_data=name=all switches, visible=False, entities=['switch.king_of_fans__inc_hbuniversalcfremote_0016c57d_1_6'], object_id=all_switches, domain=group, service_call_id=3040928720-1, service=set>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: entity_id=group.all_switches, new_state=<state group.all_switches=off; entity_id=('switch.king_of_fans__inc_hbuniversalcfremote_0016c57d_1_6',), friendly_name=all switches, auto=True, order=0, hidden=True @ 2018-02-24T11:16:23.494736-05:00>, old_state=None>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_executed[L]: service_call_id=3040928720-1>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: entity_id=light.jasco_products_45856_001f3768_1, new_state=<state light.jasco_products_45856_001f3768_1=off; friendly_name=Jasco Products 45856, supported_features=0 @ 2018-02-24T11:16:23.606151-05:00>, old_state=None>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: service_data=name=all lights, visible=False, entities=['light.jasco_products_45856_001f3768_1'], object_id=all_lights, domain=group, service_call_id=3040928720-2, service=set>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: entity_id=group.all_lights, new_state=<state group.all_lights=off; entity_id=('light.jasco_products_45856_001f3768_1',), friendly_name=all lights, auto=True, order=1, hidden=True @ 2018-02-24T11:16:23.643423-05:00>, old_state=None>
2018-02-24 11:16:23 INFO (MainThread) [homeassistant.core] Bus:Handling <Event service_executed[L]: service_call_id=3040928720-2>

However it doesn't seem to update its state when I toggle the switch, I don't see any changes when I evaluate the following template:

{{ states.binary_sensor.jasco_products_45856_001f3768_2}}

Which always outputs:

<template state binary_sensor.jasco_products_45856_001f3768_2=on; level=255 @ 2018-02-24T11:16:23.440202-05:00>

For reference here is the output from bellows devices:

Device:
  NWK: 0x4282
  IEEE: 00:22:a3:00:00:1f:37:68
  Endpoints:
    1: profile=0x104, device_type=DeviceType.ON_OFF_LIGHT
      Input Clusters:
        Basic (0)
        Identify (3)
        Groups (4)
        Scenes (5)
        On/Off (6)
        Metering (1794)
        Diagnostic (2821)
      Output Clusters:
        Time (10)
        Ota (25)
    2: profile=0x104, device_type=DeviceType.ON_OFF_LIGHT_SWITCH
      Input Clusters:
        Basic (0)
        Identify (3)
        Diagnostic (2821)
      Output Clusters:
        Identify (3)
        On/Off (6)
Device:
  NWK: 0xa0f9
  IEEE: 00:22:a3:00:00:16:c5:7d
  Endpoints:
    1: profile=0x104, device_type=14
      Input Clusters:
        Basic (0)
        Identify (3)
        Groups (4)
        Scenes (5)
        On/Off (6)
        Level control (8)
        Fan Control (514)
      Output Clusters:
        Identify (3)
        Ota (25)

I should have more time next weekend to play with it.

@Adminiuga
Copy link
Copy Markdown
Contributor

Adminiuga commented Mar 5, 2018

@igorbernstein2

Doesnt seem to work with my GE ZigBee Lighting Switch 45856GE

try removing the switch from the network with zha.remove and rejoin it. I thinks it creates the binding from client on/off cluster to NCP upon device discovery (if I'm understanding the code correctly)

@Adminiuga
Copy link
Copy Markdown
Contributor

Adminiuga commented Mar 5, 2018

I'm happy to work this to just generate events instead of create a binary_sensor, if there's some consensus that'd be better. I'm not sure how other components in hass deal with devices like this, so happy to take pointers.

binary_sensor (or sensor, I guess) has the advantage of having some common bits already wired together, but the disadvantage of not supporting "on, on" type event streams.

Is it possible to have both? I concur with the point of "having everything wired together", but in the same time somewhat miss the flexibility which "on, on" event sequence could offer.

Copy link
Copy Markdown
Contributor

@Adminiuga Adminiuga Apr 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to level command_id should be 0x00.
command_id == 0x01 is "move" command with [MoveMode(enum8), Rate(uint8)] arguments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Not sure why I had 0 there. I've changed it to 0, and added a real implementation of move. Seems to be working well with my Osram dimmer switch.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we update the 'state' since this is a "with_on_off" command?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind. I see now it's being handled in the _move_level. Although per ZCL specs, move, move_to_level, step, stop commands shall be ignored when the device is in 'Off' state.

@Adminiuga
Copy link
Copy Markdown
Contributor

@rcloran so what's the fate of this pull request? any chance it is going to be merged or is there something else in the works?

I'm running a private fork of it and I'm pretty happy with it. Just discovered some typos with LevelListener which should be pretty trivial to fix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this meant to be self._level = min(255, max(0, self._level + change)) ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Derp, yes. Thanks.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return code from bind and configure_reporting should be evaluated and not just ignored. There is a good change a device not supports it or went to sleep. At least an error log entry would be helpful

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember right, the reason this is wrapped in safe() is that some devices don't support one or the other method of setup. It's logged at info level in safe() -- I'll bump that to warning.

@rcloran
Copy link
Copy Markdown
Contributor Author

rcloran commented Apr 30, 2018

@Adminiuga : I agree having both makes sense. I'm going to clean up and merge this today, I'd be happy to review a PR that added events.

@rcloran rcloran merged commit 02a12a0 into home-assistant:dev Apr 30, 2018

_domain = DOMAIN

class OnOffListener:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why nest the listener classes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was that they are pretty tightly bound to the Switch class, and are only classes because of the zigpy API. shrug

# Normally the entity itself is the listener. Base classes may set this to
# a dict of cluster ID -> listener to receive messages for specific
# clusters separately
_in_listeners = {}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having mutable objects as class attributes is dangerous and prone to bugs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like these are used as a class wide registry of listeners. Will there never be a problem of one instance overwriting a listener of another instance? Are cluster_id s unique to each entity?

Copy link
Copy Markdown
Member

@MartinHjelmare MartinHjelmare Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it looks like we replace the class attribute with an instance attribute of the same name when instantiating the child entity. So then it's not a class wide registry anymore, which is good. But I'd suggest to rewrite this logic anyway. See my comment about a safer way to handle state update callbacks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not ever replaced.

~/src/home-assistant/homeassistant/components$ egrep -r '(in|out)_listeners' .
./zha/__init__.py:    _in_listeners = {}
./zha/__init__.py:    _out_listeners = {}
./zha/__init__.py:            cluster.add_listener(self._in_listeners.get(cluster_id, self))
./zha/__init__.py:            cluster.add_listener(self._out_listeners.get(cluster_id, self))
./binary_sensor/zha.py:        self._out_listeners = {

Anyways, doing in in async_added_to_hass would be better.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last line in the grep replaces the class attribute with an instance attribute in the entity name space.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, I see what you mean. Yes, you're right (except it doesn't need to be an instance attribute).

self._state = True
self._level = 255
from zigpy.zcl.clusters import general
self._out_listeners = {
Copy link
Copy Markdown
Member

@MartinHjelmare MartinHjelmare Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's safer to register state update callbacks in async_added_to_hass entity coroutine. If a callback is called before the entity has been added to home assistant, the call to schedule_update_ha_state will error.

I suggest you define and initialize the listener dicts (_in_listeners and _out_listeners) as instance attributes on the parent class, instead of class attributes. And set the listener items in async_added_to_hass in this child class.

self._level = 0
self._level = min(255, max(0, self._level + change))
self._state = bool(self._level)
self.schedule_update_ha_state()
Copy link
Copy Markdown
Member

@MartinHjelmare MartinHjelmare Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will these callbacks move_level, set_level etc, be called from a thread and not from within the event loop, ie that's why we're using the sync version of this method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the current radios run on a different thread. And I think it's reasonable to expect them not to. I'll update.

@balloob balloob mentioned this pull request May 11, 2018
@home-assistant home-assistant locked and limited conversation to collaborators Sep 5, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants