Skip to content

Commit

Permalink
Merge pull request #94 from grimmpp/feature-branch
Browse files Browse the repository at this point in the history
Added EEPs A5-30-01 and A5-30-03
  • Loading branch information
grimmpp authored Apr 11, 2024
2 parents f0d5d78 + 2bbc824 commit dcbbde9
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ Elatko devices are exemplarily mentioned. You can find [here](https://www.eltako

**Supported sensor EEPs**
* Binary sensor
* A5-07-01 (Occupancy sensor)
* A5-30-01, A5-30-03 (Digital Input - used for water sensor FSM60B)
* F6-02-01 ([Rocker switch](https://github.com/grimmpp/home-assistant-eltako/tree/main/docs/rocker_switch/readme.md), FTS14EM)
* F6-02-02 ([Rocker switch](https://github.com/grimmpp/home-assistant-eltako/tree/main/docs/rocker_switch/readme.md))
* F6-10-00 (Window handle, classic switches or contacs via FTS14EM, window and door contacts like FTKE, supported states: open, closed)
* D5-00-01 ([Contact sensor](https://github.com/grimmpp/home-assistant-eltako/tree/main//docs/window_sensor_setup_FTS14EM.md), FTS14EM) incl. signal inverter
* A5-07-01 (Occupancy sensor)
* Sensor
* A5-04-01 (Temperature and Humidity Sensor)
* A5-04-02 (Temperature and Humidity Sensor e.g.: FLGTF, FLT58, FFT60)
Expand Down
9 changes: 6 additions & 3 deletions changes.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Changes and Feature List

## Version 1.4.2 Added EEPs A5-30-01 and A5-30-03
* Added EEPs (A5-30-01 preferred) for digital input which is used in water sensor (FSM60B)

## Version 1.4.1 Support for sending arbitrary messages
* Added Service for sending arbitrary EnOcean (ESP2) messages. Intended to be used in conjunction with [Home Assistant Automations](https://www.home-assistant.io/getting-started/automation/).
* 🐞 Fix for TargetTemperatureSensor (EEP: A5-10-06 and A5-10-12)
* 🐞 Fix for unknow cover positions and intermediate state + unit-tests added.
* 🐞 Fix for unknown cover positions and intermediate state + unit-tests added.
* Unit-Tests added and improved for EEP A5-04-01, A5-04-02, A5-10-06, A5-10-12, A5-13-01, and F6-10-00.
* EEP A5-04-03 added for Eltako FFT60 (temperature and humidity)
* EEP A5-06-01 added for light sensor (currently twilight and daylight are combined in one illumination sensor/entity)
Expand All @@ -17,7 +20,7 @@
* Added library for ESP3 (USB300 Support) => [esp2_gateway_adapter](https://github.com/grimmpp/esp2_gateway_adapter)
* Better support for Teach-In Button

## Version 1.3.8 Fixes and Smaller Imrovements
## Version 1.3.8 Fixes and Smaller Improvements
* Fixed window handle F6-10-00 in binary sensor
* Added better tests for binary sensors
* Fixed covers which behaved differently after introducing recovery state feature.
Expand All @@ -33,7 +36,7 @@
* Trial to remove import warnings
Reported Issue: https://github.com/grimmpp/home-assistant-eltako/issues/61
* 🐞 Removed entity_id bug from GatewayConnectionState 🐞 => Requires removing and adding gateway again ❗
* Added state cache of device entities. When restarting HA entities like temperature sensors will show previouse state/value after restart.
* Added state cache of device entities. When restarting HA entities like temperature sensors will show previous state/value after restart.
Reported Feature: https://github.com/grimmpp/home-assistant-eltako/issues/63

## Version 1.3.6 Dependencies fixed for 1.3.5
Expand Down
79 changes: 74 additions & 5 deletions custom_components/eltako/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from homeassistant.const import CONF_DEVICE_CLASS, STATE_ON, STATE_OFF
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers import entity_registry as er

Expand Down Expand Up @@ -41,8 +41,39 @@ async def async_setup_entry(
try:
dev_conf = config_helpers.DeviceConf(entity_config, [CONF_DEVICE_CLASS, CONF_INVERT_SIGNAL])
if dev_conf.eep.eep_string in CONF_EEP_SUPPORTED_BINARY_SENSOR:
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, dev_conf.name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL)))
if dev_conf.eep == A5_30_03:
name = "Digital Input 0"
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL),
EntityDescription(key="0", name=name) ))
name = "Digital Input 1"
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL),
EntityDescription(key="1", name=name) ))
name = "Digital Input 2"
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL),
EntityDescription(key="2", name=name) ))
name = "Digital Input 3"
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL),
EntityDescription(key="3", name=name) ))
name = "Status of Wake"
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL),
EntityDescription(key="wake", name=name) ))
elif dev_conf.eep == A5_30_01:
name = "Digital Input"
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL),
EntityDescription(key="0", name=name) ))
name = "Low Battery"
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL),
EntityDescription(key="low_battery", name=name) ))
else:
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, dev_conf.name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL)))

except Exception as e:
LOGGER.warning("[%s] Could not load configuration for platform_id %s", platform, platform_id)
Expand Down Expand Up @@ -86,9 +117,19 @@ class EltakoBinarySensor(AbstractBinarySensor):
- D5-00-01
"""

def __init__(self, platform: str, gateway: EnOceanGateway, dev_id: AddressExpression, dev_name:str, dev_eep: EEP, device_class: str, invert_signal: bool):
def __init__(self, platform: str, gateway: EnOceanGateway, dev_id: AddressExpression, dev_name:str, dev_eep: EEP,
device_class: str, invert_signal: bool, description: EntityDescription=None):
"""Initialize the Eltako binary sensor."""
super().__init__(platform, gateway, dev_id, dev_name, dev_eep)
if description:
self.entity_description = EntityDescription(
key=description.key,
name=description.name
)
self._channel = description.key
else:
self._channel = None

super().__init__(platform, gateway, dev_id, dev_name, dev_eep, self._channel)
self.invert_signal = invert_signal
self._attr_device_class = device_class

Expand Down Expand Up @@ -237,6 +278,34 @@ def value_changed(self, msg: ESP2Message):
if self.invert_signal:
self._attr_is_on = not self._attr_is_on

elif self.dev_eep in [A5_30_01]:

if self.description_key == "low_battery":
self._attr_is_on = decoded.low_battery
else:
self._attr_is_on = decoded._contact_closed

if self.invert_signal:
self._attr_is_on = not self._attr_is_on

elif self.dev_eep in [A5_30_03]:

if self.description_key == "0":
self._attr_is_on = decoded.digital_input_0
elif self.description_key == "1":
self._attr_is_on = decoded.digital_input_1
elif self.description_key == "2":
self._attr_is_on = decoded.digital_input_2
elif self.description_key == "3":
self._attr_is_on = decoded.digital_input_3
elif self.description_key == "wake":
self._attr_is_on = decoded.status_of_wake
else:
raise Exception("[%s %s] EEP %s Unknown description key for A5-30-03", Platform.BINARY_SENSOR, str(self.dev_id), A5_30_03.eep_string)

if self.invert_signal:
self._attr_is_on = not self._attr_is_on

else:
LOGGER.warn("[%s %s] EEP %s not found for data processing.", Platform.BINARY_SENSOR, str(self.dev_id), self.dev_eep.eep_string)
return
Expand Down
2 changes: 1 addition & 1 deletion custom_components/eltako/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"iot_class": "local_push",
"issue_tracker": "https://github.com/grimmpp/home-assistant-eltako/issues",
"loggers": ["eltako"],
"requirements": ["eltako14bus==0.0.49","enocean==0.60.1", "StrEnum", "esp2-gateway-adapter==0.1"],
"requirements": ["eltako14bus==0.0.52","enocean==0.60.1", "StrEnum", "esp2-gateway-adapter==0.1"],
"version": "1.4.1"
}
9 changes: 8 additions & 1 deletion custom_components/eltako/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@
CONF_LANGUAGE,
)

CONF_EEP_SUPPORTED_BINARY_SENSOR = [F6_02_01.eep_string, F6_02_02.eep_string, F6_10_00.eep_string, D5_00_01.eep_string, A5_07_01.eep_string, A5_08_01.eep_string]
CONF_EEP_SUPPORTED_BINARY_SENSOR = [F6_02_01.eep_string,
F6_02_02.eep_string,
F6_10_00.eep_string,
D5_00_01.eep_string,
A5_07_01.eep_string,
A5_08_01.eep_string,
A5_30_01.eep_string,
A5_30_03.eep_string]
CONF_EEP_SUPPORTED_SENSOR_ROCKER_SWITCH = [F6_02_01.eep_string, F6_02_02.eep_string]

def _get_sender_schema(supported_sender_eep) -> vol.Schema:
Expand Down
4 changes: 3 additions & 1 deletion docs/service-send-message/eep-params.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Paramters for EEPs in Send Message Events
(This file was auto-generated by using [eltako14bus library](https://github.com/grimmpp/eltako14bus/blob/master/eltakobus/eep.py) in [version 0.0.49](https://pypi.org/project/eltako14bus/) from unit-test `TestSendMessageService` in file `./tests/test_send_message_service.py`).
(This file was auto-generated by using [eltako14bus library](https://github.com/grimmpp/eltako14bus/blob/master/eltakobus/eep.py) in [version 0.0.52](https://pypi.org/project/eltako14bus/) from unit-test `TestSendMessageService` in file `./tests/test_send_message_service.py`).

## Not Supported EEPs
* `A5-09-0C`
Expand All @@ -18,6 +18,8 @@
* `A5-12-02`: data_type, divisor, learn_button, measurement_channel, meter_reading
* `A5-12-03`: data_type, divisor, learn_button, measurement_channel, meter_reading
* `A5-13-01`: dawn_sensor, day_night, hemisphere, identifier, learn_button, rain_indication, sun_east, sun_south, sun_west, temperature, wind_speed
* `A5-30-01`: battery_status, contact_status, learn_button
* `A5-30-03`: digital_input_0, digital_input_1, digital_input_2, digital_input_3, learn_button, status_of_wake, temperature
* `D5-00-01`: contact, learn_button
* `F6-02-01`: energy_bow, rocker_first_action, rocker_second_action, second_action
* `F6-02-02`: energy_bow, rocker_first_action, rocker_second_action, second_action
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
aiocoap
pyserial-asyncio
eltako14bus==0.0.49
eltako14bus==0.0.52
enocean==0.60.1
homeassistant
termcolor
Expand Down
Empty file added tests/__init__.py
Empty file.
60 changes: 60 additions & 0 deletions tests/test_binary_sensor_A5_30_01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import unittest
from mocks import *
from unittest import mock
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.const import Platform
from custom_components.eltako.binary_sensor import EltakoBinarySensor
from custom_components.eltako.config_helpers import *
from eltakobus import *
from eltakobus.eep import *

from tests.test_binary_sensor_generic import TestBinarySensor

# mock update of Home Assistant
Entity.schedule_update_ha_state = mock.Mock(return_value=None)
# EltakoBinarySensor.hass.bus.fire is mocked by class HassMock


class TestBinarySensor_A5_30_01(unittest.TestCase):

def test_digital_input(self):
bs = TestBinarySensor().create_binary_sensor(A5_30_01.eep_string, EntityDescription(key="0", name="Digital Input") )

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x92\x00\x0E')
bs.value_changed(msg)

self.assertEqual(bs.is_on, True)

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x92\xFF\x0E')
bs.value_changed(msg)

self.assertEqual(bs.is_on, False)


def test_inverted_digital_input(self):
bs = TestBinarySensor().create_binary_sensor(A5_30_01.eep_string, EntityDescription(key="0", name="Digital Input") )
bs.invert_signal = True

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x92\x00\x0E')
bs.value_changed(msg)

self.assertEqual(bs.is_on, False)

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x92\xFF\x0E')
bs.value_changed(msg)

self.assertEqual(bs.is_on, True)


def test_battery(self):
bs = TestBinarySensor().create_binary_sensor(A5_30_01.eep_string, EntityDescription(key="low_battery", name="Low Battery") )

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x92\xFF\x0E')
bs.value_changed(msg)

self.assertEqual(bs.is_on, False)

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\FF\x92\x00\x0E')
bs.value_changed(msg)

self.assertEqual(bs.is_on, True)
51 changes: 51 additions & 0 deletions tests/test_binary_sensor_A5_30_03.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import unittest
from mocks import *
from unittest import mock
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.const import Platform
from custom_components.eltako.binary_sensor import EltakoBinarySensor
from custom_components.eltako.config_helpers import *
from eltakobus import *
from eltakobus.eep import *

from tests.test_binary_sensor_generic import TestBinarySensor

# mock update of Home Assistant
Entity.schedule_update_ha_state = mock.Mock(return_value=None)
# EltakoBinarySensor.hass.bus.fire is mocked by class HassMock


class TestBinarySensor_A5_30_03(unittest.TestCase):

def test_digital_input(self):

for key in ["0", "1", "2", "3", "wake"]:
bs = TestBinarySensor().create_binary_sensor(A5_30_03.eep_string, description= EntityDescription(key=key, name=key))

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x00\x1F\x08')
bs.value_changed(msg)

self.assertEqual(bs.is_on, True)

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x00\x00\x08')
bs.value_changed(msg)

self.assertEqual(bs.is_on, False)


def test_inverted_digital_input(self):

for key in ["0", "1", "2", "3", "wake"]:
bs = TestBinarySensor().create_binary_sensor(A5_30_03.eep_string, description= EntityDescription(key=key, name=key))
bs.invert_signal = True

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x00\x1F\x08')
bs.value_changed(msg)

self.assertEqual(bs.is_on, False)

msg = Regular4BSMessage(b'\00\x00\x00\x01', 0x20, b'\00\x00\x00\x08')
bs.value_changed(msg)

self.assertEqual(bs.is_on, True)

6 changes: 3 additions & 3 deletions tests/test_binary_sensor_generic.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import unittest
from mocks import *
from unittest import mock
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.const import Platform
from custom_components.eltako.binary_sensor import EltakoBinarySensor
from custom_components.eltako.config_helpers import *
Expand All @@ -16,14 +16,14 @@
class TestBinarySensor(unittest.TestCase):


def create_binary_sensor(self, eep_string:str="F6-02-01", device_class = "none", invert_signal:bool=False) -> EltakoBinarySensor:
def create_binary_sensor(self, eep_string:str="F6-02-01", device_class = "none", invert_signal:bool=False, description:EntityDescription=None) -> EltakoBinarySensor:
gateway = GatewayMock(dev_id=123)
dev_id = AddressExpression.parse("00-00-00-01")
dev_name = "device name"

dev_eep = EEP.find(eep_string)

bs = EltakoBinarySensor(Platform.BINARY_SENSOR, gateway, dev_id, dev_name, dev_eep, device_class, invert_signal)
bs = EltakoBinarySensor(Platform.BINARY_SENSOR, gateway, dev_id, dev_name, dev_eep, device_class, invert_signal, description)
bs.hass = HassMock()
self.assertEqual(bs._attr_is_on, None)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_sensor_A5_04_03.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class TestSensor_A5_04_02(unittest.TestCase):

msg1 = Regular4BSMessage (address=b'\xFF\xFF\x00\x80', data=b'\x99\x02\x12\x09', status=0x00)
msg1 = Regular4BSMessage(address=b'\xFF\xFF\x00\x80', data=b'\x99\x02\x12\x09', status=0x00)

def create_temperature_sensor(self) -> EltakoTemperatureSensor:
gateway = GatewayMock()
Expand Down

0 comments on commit dcbbde9

Please sign in to comment.