sputnikdev / eclipse-smarthome-bluetooth-binding

Eclipse SmartHome Bluetooth Binding
46 stars 10 forks source link

Add support for Dolphin/EnOcean PTM 215B #73

Open pfink opened 5 years ago

pfink commented 5 years ago

I have some battery-less wall switches based on Dolphin/EnOcean PTM 215B that I'd like to integrate (technical manual can be found here).

My setup:

Some information that I found out so far:

@vkolotov: I'm a ESH/openHAB developer, but I'm quite new to the BLE protocol. If you could give me some hints and point me to the right direction, I may could come up with a PR.

vkolotov commented 5 years ago

Two things needs to be addressed:

pfink commented 5 years ago

But I don't really understand why your workaround is necessary at all? Or what does it really work around? From my perspective, it's "by design" that BLE devices only send data if absolutely required. So, by design, of course there can be devices which do not send at least every 20 seconds. Conclusion: The devices do not get stale, they just have to remain active because (by design) you never know when a BLE device will send data again. So you always have to listen.

Wouldn't it also be just possible to let all BLE devices that are added to openHAB as Things remain active forever (until they get removed / disabled by the user)?

vkolotov commented 5 years ago

Fund a bug, basically the binding does not subscribe to the event (manufacurer data). This simple. Building now a new snapshot.

vkolotov commented 5 years ago

But I don't really understand why your workaround is necessary at all? Or what does it really work around? From my perspective, it's "by design" that BLE devices only send data if absolutely required.

Believe or not, but your case is an exception, normally BT devices are always online and advertise something. As I mentioned above, TinyB (I suspect) native objects gets stale, irrespectively of a type of the device. Hence there is a workaround.

vkolotov commented 5 years ago

Please grub this: https://oss.sonatype.org/content/repositories/snapshots/org/sputnikdev/org.eclipse.smarthome.binding.bluetooth/1.1.7-SNAPSHOT/org.eclipse.smarthome.binding.bluetooth-1.1.7-20181203.223729-4.jar

vkolotov commented 5 years ago

There is a project started by @xrucka to get rid of TinyB layer and talk to DBus directly: https://github.com/xrucka/bluetooth-manager-dbus.

I could not find enough time to test it, it was working for me mostly, but there were a couple of issues still... Would be great if we could ditch TinyB...

pfink commented 5 years ago

normally BT devices are always online and advertise

BT devices (without BLE) -> yes BLE devices -> I'd say, sometimes, sometimes no

Hence there is a workaround.

But workaround for what? As I said, I think it's not a bug, it's feature! So what would happen if you just don't dispose things as long as they're added to openHAB? What would stop working then?

vkolotov commented 5 years ago

The bug is when you get an instance of a BT object from TinyB library, subscribe for some events, it works for some time (you get events) but then for some reason (not always though, it might work for days) it becomes stale and the object no longer notify you (even if the actual device is online). Hence the workaround to check when an "interaction" was last time and if it exceeded a timeout, destroy the object and acquire new one.

pfink commented 5 years ago

Ahh, okay. Now I understand :)

vkolotov commented 5 years ago

Let me know if the build above fixes the issue, I do not have time to test it now, I'm having a course now till the end of this week.

pfink commented 5 years ago

It doesn't fix the issue. It seems that it gets filled once, but then it's not updated anymore.

xrucka commented 5 years ago

@pfink @vkolotov I pretty much rewrote it from the scratch, however have not published it yet (there is still a lot of prooving-ground catch-all code involved), I'll push it to the main repo once I get home today.

xrucka commented 5 years ago

@pfink : if you want to try it: xrucka/bluetooth-manager-dbus branch rewrite_all + xrucka/eclipse-smarthome-bluetooth-binding-dbus-transport rewrite_all

Should work with bluez-5.48+, emulating the battery service. It's not however updated against Vlad's most recent changes, so some things might not work out of the box.

pfink commented 5 years ago

Tried BlueGiga with the online channel meanwhile. First test results are worse than tinyB in terms of reliability, it seems like it suffers even more from the "first click not recognized" issue. Will gather more data on the weekend.

EDIT: This time, reducing Bluetooth devices update rate seems to have a quite significant effect. Will continue observing...

vkolotov commented 5 years ago

I'll try to fins some time this weekend to implement that setting which turns off the workaround for things.

pfink commented 5 years ago

@vkolotov: If you find no time, pls tell me, then I can implement it :) But I'd highly recommend you to set a license for this project before, otherwise you'll run into a legal mess that you can't get out without asking all contributors for permission...

pfink commented 5 years ago

FTR: I'm currently testing with BlueGiga and an own build where I completely deactivated the reset of stale devices. First results are quite good. Is there a reason why the "stale devices" workaround is enabled by default for the BlueGiga adapter as well? Doesn't it generally effect reliability negatively, e.g. if telegrams arrive exactly at the moment when the device is reset?

vkolotov commented 5 years ago

Hi @pfink, the reason is simple. Transport implementations are supposed to be as simple as possible, focusing on providing an abstract layer between BM and hardware, hence less threads/logics. I'd suggest to keep that logic there in BM for now and implement a setting for device governors so that we can switch it off (a setting per bt device in OH).

I'm hoping that when we Dbus transport is done and stable, we will get rid of that logic completely.

vkolotov commented 5 years ago

But I'd highly recommend you to set a license for this project before, otherwise you'll run into a legal mess that you can't get out without asking all contributors for permission...

what would be your recommendations? All bits and pieces are licensed with Apache v2.

pfink commented 5 years ago

what would be your recommendations?

As licensing is a quite complex topic, I carved out the answer to #74 :)

pfink commented 5 years ago

I'm hoping that when we Dbus transport is done and stable, we will get rid of that logic completely.

Okay. I'll start testing bluetooth-manager-dbus as soon as I completed the reliability test of my current setup. This will take probably take at least one more week, because I have to test under everday life conditions.

vkolotov commented 5 years ago

Great, thank you, let us know how it goes.

pfink commented 5 years ago

Reliability of BlueGiga with deactivated stale device removals is very good :) Out of 60 clicks in the last days, just a single one failed (with online channel).

@vkolotov: Anyhow, ManufacturerData still do not arrive at the item. @xrucka: Tested with dbus, but the buttons do not get discovered. Log output:

2018-12-21 22:01:25.709 [INFO ] [.manager.transport.dbus.BluezFactory] - Found running bluetooth daemon, connecting & populating...
2018-12-21 22:01:30.709 [DEBUG] [.manager.transport.dbus.BluezFactory] - Discovered devices:
2018-12-21 22:01:30.709 [DEBUG] [.manager.transport.dbus.BluezFactory] - Discovered adapters:

(repeats every few seconds)

I tested it with Bluez 5.50.

Now I'll start to test tinyB with deactivated stale device removals as well.

vkolotov commented 5 years ago

Great news. Keeps up posed please.

Re manufacture data, I believe there is a bug somewhere. I might have some time next week to work on it, it should be something obvious...

pfink commented 5 years ago

Findings regarding manufacturer data so far:

pfink commented 5 years ago

More findings:

pfink commented 5 years ago

I did setup PR #75 now which fixes the issue. Anyhow, I'm not sure if this is a proper fix or just a workaround. @vkolotov: Would be cool if you could take a look on it :)

pfink commented 5 years ago

Good news: With the bugfix and some Binary parsing with Jython / JSR223, I'm now able to obtain which button was pressed / released 👍

pfink commented 5 years ago

FTR, Textual Configuration works like this:

Thing bluetooth:ble:E215000053B1 "My EnOcean PTM 215B Button" {
    Channels:
        Type characteristic--------readable-000003da-0000-0000-0000-000000000000-null: 3DA-3DA "Binary Button Data"
}
pfink commented 5 years ago

FTR, this is a Python / JSR223 script which parses the binary data and fills other items with human-readable String's that tell what actually happened:

from org.slf4j import LoggerFactory
import re

scriptExtension.importPreset("RuleSupport")
scriptExtension.importPreset("RuleSimple")

class MyRule(SimpleRule):
    def __init__(self, rawItemName, targetItemName):
        self.rawItemName = rawItemName
        self.targetItemName = targetItemName

        self.triggers = [
            TriggerBuilder.create()
                    .withId(rawItemName + "-changed")
                    .withTypeUID("core.ItemStateUpdateTrigger")
                    .withConfiguration(
                        Configuration({
                            "itemName": self.rawItemName
                        })).build()
        ]

    def execute(self, module, input):
        state = str(ir.getItem(self.rawItemName).getState())
        num_of_bits = 8
        switch_status_hex = state.replace("[", "").replace("]", "").split(", ")[4]
        switch_status_bin = bin(int(switch_status_hex, 16))[2:].zfill(num_of_bits)
        buttons = {
            3: "B1",
            4: "B0",
            5: "A1",
            6: "A0",
        }

        for button in buttons.keys():
            if switch_status_bin[button] == "1":
                button_activated = buttons[button]                

        operation = "PRESSED" if switch_status_bin[7] == "1" else "RELEASED"

        LoggerFactory.getLogger("de.pfink.PTM215BParser").debug("Button Operation: " + button_activated + " " + operation)
        events.postUpdate(self.targetItemName, button_activated + " " + operation)

items = ir.getItem("PTM215B_Button_Events").getAllMembers()

for item in items:    
    targetItemName = item.getName()
    rawItemName = targetItemName + "_Raw"
    automationManager.addRule(MyRule(rawItemName, targetItemName))

Sample Item Configuration:

Group PTM215B_Button_Events
String Button_Bedroom_Raw  "Button_Bedroom_Raw [%s]"    { channel="bluetooth:ble:E215000053D1:3DA-3DA" }
String Button_Bedroom     "Button_Bedroom [%s]"    (PTM215B_Button_Events)

This is how it works: For all items that are member of group PTM215B_Button_Events,it tries to find a corresponding item with the same name and the suffix _Raw. Then, a rule to that item is attached and listens to the incoming binary data. If the rule is trigerred, it parses the data and writes human-readable strings into the item which is member of PTM215B_Button_Events.