Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add F6 and D5 sensor types to the enocean integration #32207

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
112 changes: 69 additions & 43 deletions homeassistant/components/enocean/binary_sensor.py
Expand Up @@ -14,6 +14,8 @@

_LOGGER = logging.getLogger(__name__)

CONF_TYPE = "type"

DEFAULT_NAME = "EnOcean binary sensor"
DEPENDENCIES = ["enocean"]
EVENT_BUTTON_PRESSED = "button_pressed"
Expand All @@ -23,6 +25,7 @@
vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_TYPE, default=2): cv.positive_int,
}
)

Expand All @@ -32,22 +35,23 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
dev_id = config.get(CONF_ID)
dev_name = config.get(CONF_NAME)
device_class = config.get(CONF_DEVICE_CLASS)
device_type = config.get(CONF_TYPE)

add_entities([EnOceanBinarySensor(dev_id, dev_name, device_class)])
add_entities([EnOceanBinarySensor(dev_id, dev_name, device_class, device_type)])


class EnOceanBinarySensor(enocean.EnOceanDevice, BinarySensorDevice):
"""Representation of EnOcean binary sensors such as wall switches.

Supported EEPs (EnOcean Equipment Profiles):
- F6-02-01 (Light and Blind Control - Application Style 2)
- F6-02-02 (Light and Blind Control - Application Style 1)
"""

def __init__(self, dev_id, dev_name, device_class):
def __init__(self, dev_id, dev_name, device_class, device_type):
"""Initialize the EnOcean binary sensor."""
super().__init__(dev_id, dev_name)
self._device_class = device_class
self._device_type = device_type
self.which = -1
self.onoff = -1

Expand All @@ -61,53 +65,75 @@ def device_class(self):
"""Return the class of this sensor."""
return self._device_class

@property
def device_type(self):
"""Return the type of this sensor."""
return self._device_type

def value_changed(self, packet):
"""Fire an event with the data that have changed.

This method is called when there is an incoming packet associated
with this platform.

Example packet data:
- 2nd button pressed
['0xf6', '0x10', '0x00', '0x2d', '0xcf', '0x45', '0x30']
- button released
['0xf6', '0x00', '0x00', '0x2d', '0xcf', '0x45', '0x20']
"""
# Energy Bow
pushed = None

if packet.data[6] == 0x30:
pushed = 1
elif packet.data[6] == 0x20:
pushed = 0

self.schedule_update_ha_state()

action = packet.data[1]
if action == 0x70:
self.which = 0
self.onoff = 0
elif action == 0x50:
self.which = 0
self.onoff = 1
elif action == 0x30:
self.which = 1
self.onoff = 0
elif action == 0x10:
self.which = 1
self.onoff = 1
elif action == 0x37:
self.which = 10
self.onoff = 0
elif action == 0x15:
self.which = 10
self.onoff = 1
self.hass.bus.fire(
EVENT_BUTTON_PRESSED,
{
"id": self.dev_id,
"pushed": pushed,
"which": self.which,
"onoff": self.onoff,
},
)

if packet.data[0] == 0xf6:
# Energy Bow
pushed = None
pressed = None
first_action = None
second_action = None
second_action_valid = None


if self._device_type == 2:
packet.parse_eep(0x02, 0x02)
pressed = packet.parsed['EB']['raw_value']
first_action = packet.parsed['R1']['raw_value']
second_action = packet.parsed['R2']['raw_value']
second_action_valid = packet.parsed['SA']['raw_value']

if packet.data[6] == 0x30:
pushed = 1
elif packet.data[6] == 0x20:
pushed = 0

self.schedule_update_ha_state()

action = packet.data[1]
if action == 0x70:
self.which = 0
self.onoff = 0
elif action == 0x50:
self.which = 0
self.onoff = 1
elif action == 0x30:
self.which = 1
self.onoff = 0
elif action == 0x10:
self.which = 1
self.onoff = 1
elif action == 0x37:
self.which = 10
self.onoff = 0
elif action == 0x15:
self.which = 10
self.onoff = 1

self.hass.bus.fire(
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't change the code around this until the integration has been refactored to not fire events from entities. This has been discussed in earlier PRs for this integration.

Custom events can be fired from a non platform module in the integration and it shouldn't be linked to an entity. See eg the deconz integration.

https://github.com/home-assistant/home-assistant/blob/16dba3fa8519151cff4064fcaccfb194e4bd0ba0/homeassistant/components/deconz/deconz_event.py#L56

Copy link
Author

Choose a reason for hiding this comment

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

Don't really know about the events fired from entities.
What I saw is a lot of hardcoding in the file, that is not present in all enocean files (others use enocean API).
That's why I used the API parsing methods that do it all in one line and avoid the if/elif code that's now in this file. Doing that, as I said I avoided breaking everything so I left the existing code and rather added new variables un the event that's fired.

With the new variables, for automation I currectly only need thses two to know which button has been pressed and not launch twice the actions (button pressed and then button released):

      pressed: 1
      first_action: 0

Copy link
Member

Choose a reason for hiding this comment

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

Just reiterating that we will not accept changes to the event firing in this integration, until that has been changed to be in line with our design rules.

Please remove the event fire changes from this PR. A separate PR needs to be done first where the event firing is reworked.

EVENT_BUTTON_PRESSED,
{
"id": self.dev_id,
"pushed": pushed,
"which": self.which,
"onoff": self.onoff,
"pressed": pressed,
"first_action": first_action,
"second_action": second_action,
"second_action_valid": second_action_valid,
}
)
48 changes: 41 additions & 7 deletions homeassistant/components/enocean/sensor.py
Expand Up @@ -32,31 +32,43 @@
SENSOR_TYPE_POWER = "powersensor"
SENSOR_TYPE_TEMPERATURE = "temperature"
SENSOR_TYPE_WINDOWHANDLE = "windowhandle"
SENSOR_TYPE_DOOR_WINDOW = "door"

SENSOR_TYPES = {
SENSOR_TYPE_HUMIDITY: {
"name": "Humidity",
"unit": "%",
"icon": "mdi:water-percent",
"class": DEVICE_CLASS_HUMIDITY,
"orig_state": None,
},
SENSOR_TYPE_POWER: {
"name": "Power",
"unit": POWER_WATT,
"icon": "mdi:power-plug",
"class": DEVICE_CLASS_POWER,
"orig_state": None,
},
SENSOR_TYPE_TEMPERATURE: {
"name": "Temperature",
"unit": TEMP_CELSIUS,
"icon": "mdi:thermometer",
"class": DEVICE_CLASS_TEMPERATURE,
"orig_state": None,
},
SENSOR_TYPE_WINDOWHANDLE: {
"name": "WindowHandle",
"unit": None,
"icon": "mdi:window",
"class": None,
"orig_state": None,
},
SENSOR_TYPE_DOOR_WINDOW: {
Copy link
Member

Choose a reason for hiding this comment

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

How many states does this sensor have?

Copy link
Author

Choose a reason for hiding this comment

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

The WINDOWHANDLE type or the one I added DOOR_WINDOW ?
The WINDOWHANDLE has various I think but I don't have the details (like opened, closed, inclination...)
The new DOOR_WINDOW one has two : Opened and Closed.

Regards.

Copy link
Member

Choose a reason for hiding this comment

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

I'm referring to SENSOR_TYPE_DOOR_WINDOW. If it only has two states it should just be a binary sensor and part of the binary sensor platform.

Copy link
Author

Choose a reason for hiding this comment

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

I thought about that but had problems because binary sensors has no real state as someone told me in the Discord.
In my tests, I would open the door for instance, the sensor would go to "opened" state, but it would go back to the original state after a few seconds, and the person told me it was normal, that I had to create another "binary_sensor" in HA that would have to be updated based on automation fired by the binary sensor event of enocean...
As I understood, the enocean binary sensor is just an event, like a push of a button that instantly goes back to "not pressed" after releasing.

Copy link
Member

Choose a reason for hiding this comment

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

That's the problem with the current design of the binary sensor platform in this integration. If the device is state less and just fires events, it should not be a binary sensor entity. It should just be a custom home assistant event.

Copy link
Author

Choose a reason for hiding this comment

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

I understand... So between this and the other integration "design rules" problem, I should just remove the PR, no ?
But is anyone working on the integration's rewrite ? Because with these changes at least other devices work in HA... But I understand your point that if people keep adding stuff instead of rewriting the integration, you think nobody will ever do it, right ?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure how much would be left of this PR with this limitation.

I don't believe anyone is currently working on this integration.

Yes, unfortunately that's how we have to act to avoid getting even further away from our architecture when we have legacy design.

Copy link
Author

Choose a reason for hiding this comment

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

ok that's a shame, so I'll just keep on using my local changes to enjoy my devices :-p

"name": "DoorWindow",
"unit": None,
"icon": "mdi:door-closed",
"class": None,
"orig_state": STATE_CLOSED,
},
}

Expand Down Expand Up @@ -101,6 +113,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None):

elif sensor_type == SENSOR_TYPE_WINDOWHANDLE:
add_entities([EnOceanWindowHandle(dev_id, dev_name)])

elif sensor_type == SENSOR_TYPE_DOOR_WINDOW:
add_entities([EnOceanDoorWindow(dev_id, dev_name)])


class EnOceanSensor(enocean.EnOceanDevice):
Expand All @@ -114,7 +129,7 @@ def __init__(self, dev_id, dev_name, sensor_type):
self._dev_name = f"{SENSOR_TYPES[self._sensor_type]['name']} {dev_name}"
self._unit_of_measurement = SENSOR_TYPES[self._sensor_type]["unit"]
self._icon = SENSOR_TYPES[self._sensor_type]["icon"]
self._state = None
self._state = SENSOR_TYPES[self._sensor_type]["orig_state"]

@property
def name(self):
Expand Down Expand Up @@ -147,7 +162,6 @@ def value_changed(self, packet):

class EnOceanPowerSensor(EnOceanSensor):
"""Representation of an EnOcean power sensor.

EEPs (EnOcean Equipment Profiles):
- A5-12-01 (Automated Meter Reading, Electricity)
"""
Expand All @@ -171,7 +185,6 @@ def value_changed(self, packet):

class EnOceanTemperatureSensor(EnOceanSensor):
"""Representation of an EnOcean temperature sensor device.

EEPs (EnOcean Equipment Profiles):
- A5-02-01 to A5-02-1B All 8 Bit Temperature Sensors of A5-02
- A5-10-01 to A5-10-14 (Room Operating Panels)
Expand All @@ -180,7 +193,6 @@ class EnOceanTemperatureSensor(EnOceanSensor):
- A5-10-10 (Temp. and Humidity Sensor and Set Point)
- A5-10-12 (Temp. and Humidity Sensor, Set Point and Occupancy Control)
- 10 Bit Temp. Sensors are not supported (A5-02-20, A5-02-30)

For the following EEPs the scales must be set to "0 to 250":
- A5-04-01
- A5-04-02
Expand Down Expand Up @@ -210,7 +222,6 @@ def value_changed(self, packet):

class EnOceanHumiditySensor(EnOceanSensor):
"""Representation of an EnOcean humidity sensor device.

EEPs (EnOcean Equipment Profiles):
- A5-04-01 (Temp. and Humidity Sensor, Range 0掳C to +40掳C and 0% to 100%)
- A5-04-02 (Temp. and Humidity Sensor, Range -20掳C to +60掳C and 0% to 100%)
Expand All @@ -232,7 +243,6 @@ def value_changed(self, packet):

class EnOceanWindowHandle(EnOceanSensor):
"""Representation of an EnOcean window handle device.

EEPs (EnOcean Equipment Profiles):
- F6-10-00 (Mechanical handle / Hoppe AG)
"""
Expand All @@ -243,7 +253,7 @@ def __init__(self, dev_id, dev_name):

def value_changed(self, packet):
"""Update the internal state of the sensor."""

action = (packet.data[1] & 0x70) >> 4

if action == 0x07:
Expand All @@ -254,3 +264,27 @@ def value_changed(self, packet):
self._state = "tilt"

self.schedule_update_ha_state()

class EnOceanDoorWindow(EnOceanSensor):
"""Representation of an EnOcean door sensor device.
EEPs (EnOcean Equipment Profiles):
- D5-00-01
"""

def __init__(self, dev_id, dev_name):
"""Initialize the EnOcean door and window sensor device."""
super().__init__(dev_id, dev_name, SENSOR_TYPE_DOOR_WINDOW)
self._dev_name = dev_name

def value_changed(self, packet):
"""Update the internal state of the sensor."""
packet.parse_eep(0x00, 0x01)
contact_value = packet.parsed['CO']['value']
if contact_value == 'open':
self._state = STATE_OPEN
self._icon = "mdi:door-open"
elif contact_value == 'closed':
self._state = STATE_CLOSED
self._icon = "mdi:door-closed"

self.schedule_update_ha_state()