Jablotron JA-80 series and JA-100 series alarm integration

I’ve tried this one repeatedly, but it reacts the same way as \x00\x00\x01\x01: I still need to pause it about 10 seconds before sending the next message.

@Marcel1: I’d happy to decipher all of your packets, but I’m still unable to find the right payload to trigger all of the \x52\x09\x8a packets. It’s good to see though that your sensor packets start with the same bytes as mine.

@plaksnor: Totally clear now.
I experience the same as you indeed.
Only startup code \x80\x01\x01\x52\x01\x0E seems to generate a response every time, regardless the frequenty of submitting. Good stuff. How exactly did you find this code?

I just tried to modify the watcherloop and decreased the waits:

def _watcher_loop(self):

    while not self._stop.is_set():
        
        if not self._data_flowing.wait(2):
            _LOGGER.warn("Data has not been received for 2 seconds, retry startup message")
            self._startup_message()         
        else:
           _LOGGER.debug("Data is flowing, wait 1 second before checking again")
           time.sleep(1)

I haven’t seen the control panel being so responsive to HA commands before.
This is really a good improvement.

I am wondering, is there still a need for checking if data is flowing? Should’t you just send the new startup code every second?

@Marcel1: Thanks for the confirmation. Good to know our systems respond exactly the same. The way I found this was by trial and error: I just submitted (parts of) packets which have been captured in the logfiles as sent (not received) packages. Somehow I’ve missed this one.
The other thing I noticed yesterday was the range of bytes the 100-series seems to use for sending commands to the alarm system:

\x80\x01\x01\x52\x01\x0E		startup message
\x80\x08\x03\x39\x39\x39		pre code for sending alarmcode
\x80\x02\x0d\x90				disarm
\x80\x02\x0d\xa0				arm away
\x80\x02\x0d\xb0				arm home

They all start with \x80, so this makes it easier to search.

Watcher loop:
I’m not sure if we need to check if data is flowing. I thought @Matt has no reason to repeatedly send a keepalive packet, because the 80-series seems to have a continious stream of data.
@Matt: any thoughts how we could make this more generic? I do not mind having different logic for different JA-series, but in general I’d expect both should work the same way.

JA system information:
Are you guys also seeing system information when you’re submitting this?

echo -ne "\x30\x01\x01\x30\x01\x02\x30\x01\x03\x30\x01\x04\x30\x01\x05\x30\x01\x08\x30\x01\x09\x30\x01\x0A\x30\x01\x0B\x30\x01\x0C\x30\x01\x11\x52\x03\x1A\x01\x00\x3C\x01\x01\x00" > /dev/hidraw0

The lines below represent a part of the packet I found last night, which contains additional info which might been interesting to show in HA.

30 01 01 30 01 02 30 01 03 30 01 04 30 01 05 30   0..0..0..0..0..0
01 08 30 01 09 30 01 0A 30 01 0B 30 01 0C 30 01   ..0..0..0..0..0.
11 52 03 1A 01 00 3C 01 01 00 00 00 00 00 00 00   .R....<.........
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 

It will respond with my model number

0000c640  40 0c 02 4a 41 2d 31 30  31 4b 2d 4c 41 4e 00 00  |@..JA-101K-LAN..|

My FW (firmware) version:

0000c740  40 08 08 4c 4a 36 30 34  32 32 00 10 f4 00 00 00  |@..LJ60422......|

My HW (hardware) version:

0000c780  40 08 09 4c 4a 31 36 31  32 33 00 10 f4 00 00 00  |@..LJ16123......|

And further on it will also show my Registration Code (format XXXXX-XXXXX-XXXX) and the name of my Configuration, for example “Centrale Lastname”.

That’s all for now, folks :slight_smile:

Ok. Great catch. Like i said, good performance/response improvement.

The JA system information is showing here as well when sending your code:

JA-101K
LJ60422
LJ16120
…and indeed the registration code.

Maybe we’ll find a usecase later where this information can be usefull!

I think I got some new progress.
The upgrade of the new startup/keepalive message was good, but only for getting info about the alarm state. This afternoon I thought: what if we find a packet which gives us info about sensor states, but with the same high rate frequency as the recently discovered packet?

So I analyzed things over again and applied a different keepalive packet:

echo -ne "\x80\x01\x02" > /dev/hidraw0

The system responds with 2 relevant types of packets. And for what I see, they only appear when you trigger sensors. So I extended the _read function with additional conditions:

                elif packet[:2] == b'\x55\x09' or (packet[:2] == b'\xd8\x08' and packet[10:12] == b'\x55\x09'): # sensor?
                  
                    # offset is different when packet starts with d8 08
                    if packet[:2] == b'\x55\x09':
                        sensordata = packet[0:6]
                    else:
                        sensordata = packet[10:16]

                    # get info
                    _devtyp = sensordata[2:3]
                    _state  = sensordata[3:4]
                    _device = sensordata[4:6]

                    # not sure what the 3rd byte is yet, let's try this
                    if _devtyp == b'\x00':
                        devtyp = 'magnetic or PIR'
                    elif _devtyp == b'\x01':
                        devtyp = 'PIR with photo'
                    else:
                        devtyp = 'unknown'

                    if _state == b'\x88' or _state == b'\x80':
                        sensor_state = 'opened'
                    elif _state == b'\x8a' or _state == b'\x82':
                        sensor_state = 'closed'
                    elif _state == b'\x75' or _state == b'\x79' or _state == b'\x7d':
                        sensor_state = 'triggered'
                    else:
                        sensor_state = 'unknown'

                    # most probably user specific
                    if _device == b'\x00\x02':
                        device = 'backdoor'
                    elif _device == b'\x80\x01':
                        device = 'frontdoor'
                    elif _device == b'\x40\x01':
                        device = 'studio'
                    elif _device == b'\xc0\x00':
                        device = 'hall'
                    elif _device == b'\x00\x01':
                        device = 'garage'
                    else:
                        device = 'unknown'

                    _LOGGER.info("Sensor changed: packet info: %s %s %s", _devtyp, _state, _device)
                    _LOGGER.info("Sensor changed: resolves to: %s %s %s", devtyp, sensor_state, device)
                    pass

Is someone able to test this by using the new startup message and applying the above code as extended condition in the _read function? I’ll change my github repo later on when I’ve done some more tests.

To confirm, the JA-80 doesn’t need the data flowing tests, that was added for the JA-100 series control panels, which I don’t have access to.

Given all the great progress you’ve made on the JA-100 series, shall we take this opportunity to rename the repo to remove the 80 designation. I wanted to check as it might require you to reclone the repo (though somethings in GitHub are resilient to such name changes, last time I check clone repos weren’t)

Sure, great suggestion!
Another suggestion for new development: could we make it as a multiple platform? At the moment, it’s an alarm_control_panel platform, but I’d like to extend it with a sensor platform as it looks like we’re also getting sensor data.

Ps. I noticed I was using ‘state’ as variable in my last post, but that variable was already being used and could mess up your alarm state :sweat_smile:

Progress on the sensor…
image

Concept of the multiple platform component, please let me know your thoughts:

configuration.yaml:
  • defines location of the serial port
  • reference to the alarm code (preferable by using !secret code in secrets.yaml)
  • specifies arm/disarm code requirements

__init__.py:

  • identifies the alarm system, for example: 80 or 100 series.
  • loads platforms:
    – Is polling for the alarm state, for example b’\x80\x01\x01\x52\x01\x0E’ (in case of 100-series)
    – Is polling for the sensor states, for example b’\x80\x01\x02’ (in case of 100-series). This should be done once, so regardless how many sensors your system has. Therefore it cannot be part of the binary_sensor.py script (right?)

alarm_control_panel.py

  • updates alarm state, based on triggers in the init.

binary_sensor.py

  • updates sensor states, based on triggers in the init.

@plaksnor: I have changed the startupcode and extended the _read function as suggested.
I triggered all my sensors, my log only shows unknown packages:

2019-05-29 22:48:48 DEBUG (Thread-25) [custom_components.jablotron.alarm_control_panel] Unknown packet: b'\x80\x01\x02\x00\x00\x00\x00\x00T)\x00\x10\xf4\x00\x00\x00\x00\x00\x00\x00\x0c)\x00\x10\x00\x00\x00\x00\x0c\x00\x00\x00\x01\x00\x00\x00\xd9\xd3\x00\x00\x00\x00\x00\x00d\x04\x00\x10\x0b\x00\x00\x00|E\x00\x10\x00\x00\x00\x00\x97R\x03\x00'

I will try to troubleshoot some time later.

Some quick remarks that I want to share. It might be helpfull to you.
In your code you commented that you are not sure what the 3rd byte is.
In my earlier post, i mentioned that my wired magnetic sensors send out messages starting with 550800 while my wireless magnetic sensors send out messages starting with 550901. Looks like both byte 2 and 3 are related to wirless vs wired.

Also i noticed that you assume that byte 4 is the sensor state.
This might be true, but i’ve noticed the value of the 4th byte is not consistent across sensors.

In my case:
wired magnatic sensor byte #4 open: 8c
wired magnatic sensor byte #4 close: 8e
wireless magnetic sensor-1 byte #4 open: 80
wireless magnetic sensor-1 byte #4 close: 82
wireless magnetic sensor-2 byte #4 open: 84
wireless magnetic sensor-2 byte #4 close: 86
For me the closed state is always open state +2

Out of curiosity: Is there a particular reason you are deciphering the 55 codes here? The 52 codes (only generated by wirless sensors) seem easier to decpipher. Or do you have all your sensors wired?

One quick probably stupid question (sorry for being a python noob :slight_smile: )
I understand that packet[:2] means: first 2 bytes
But how do I exactly read: packet[10:12]?
Is it something like: start reading after byte 10, till byte 12? So that the actual result is that you catch byte 11 and 12?

@marcel1: sorry, I had to mention you need to change some packet conditions, my bad!

The old script contains the following condition, but we actually need packet b\ ‘xd8’

elif packet[:1] == b'\xd0' or packet[:1] == b'\xd2' or packet[:1] == b'\xd8':
    pass # recognised, but as yet undeciphered JA-101 packets 

So I changed this condition to

elif packet[:1] == b'\xd0' or packet[:1] == b'\xd2':
    pass # recognised, but as yet undeciphered JA-101 packets 

And I added a new condition for the sensors only

elif packet[:2] == b'\x55\x09' or (packet[:2] == b'\xd8\x08' and packet[10:12] == b'\x55\x09'):

What it actually scans for is:

  • packets which start with 55 09 or
  • packets which start with d8 08 and have 55 09 on position 11 and 12 (this seems to be the only anomaly)

So packet[10:12] is from packet 11 (remember, start counting at 0) til 12 (not including). I also had to test this before I used it.

I checked your analysis before analyzing my data, but it seems to be different.

  • All sensors I tested were wireless and starting in the 55 09 range.
  • At the moment, I don’t have any messages starting with 52 or 55 08. I’ve added them in my script to keep an eye on them. Maybe I miss important data we could use :slight_smile:

Things in common:

  • I have tested 2 doors with wireless magnetic sensors, but both seem to have different bytes for opening and closing it. But at least they seem to be consistent per device.
  • I also noticed 1 instead of 2 different bytes for PIR sensors. So for now I’ve build a time out after 1 sec.

This afternoon I’ve made some progress. Pretty hard with zero python skills, but there are some good examples online. It just takes a while to figure out how things work and how I think we could make this work for anyone, without sniffing packets.
So I decided to make a copy of @matt’s Jablotron component to build a multiple platform component in order to add binary sensors (on/off/opened/closed/etc.). Here you’ll see a working PIR sensor as a binary sensor:

Later on I thought, as a user you don’t want to define all your sensors one by one. And if you would, how would you even know how to identify them? You don’t want users to sniff their packets like we did, so we need to make something what’s more dynamic.
If HA finds a unknown packet starting with 55 09, it’s probably a device which hasn’t been triggered yet. So I used the 5th and 6th byte to give the binary sensor an ID. You could always give it a friendly name and a device class (door/window/motion):


Here you can see new binary sensors incoming as soon you trigger them. Sensor 40_01 is a wireless PIR, 00 02 is a wireless magnetic door sensor and 80_01 is another wireless magnetic door sensor I opened.
Please ignore the ‘binary_sensor.jablotron_door_sensor’ sensor. This was the original binary sensor catching all 55 09 packets regardless the device or state.

Pro’s: as a user you don’t need to setup all sensors manually in configuration.yaml. The script will currently create new binary sensors as soon as it receives the right packets.
Con’s: after a reboot, all your binary sensors are gone. Your history is still there, but sensors will be shown as soon as they report. Which could be never if you won’t open all windows, doors, etc.

So I guess we need to decide how we want to take this to another level.
@matt: do you have any idea what we should do? At the moment, I used set_state to create any binary sensor we want, but I’m not sure if this would cause any problems later on.

In the meanwhile, I’ll work on my python skills, try to add more functionality (icons, attributes) keep an eye on unknown packets and try to work a new release to publish/test.

hi @plaksnor.
I had already removed b'\xd8 from the pass condition, that was not the problem.

I’ve noticed that whatever i do or try, my control panel is not generating any \x55\x09 codes, also not as part of a \xd8\x08 code even though i did measure those codes when i was connected to F-link.

I am starting to believe that what i’ve measured when conncted to F-link (\x55\x09, \x52\x09 and \xd8\x08 with an \x55\x09 in byte 10 and 11) is somehow for me only generated when connected to F-link.
I am a bit lost at this point and need time to investigate this.

I got the OEM usb serial cable yesterday and have quickly setup new RPi with Hassio for testing with my JA-80. Arm away was successful :grinning:. When I Disarm back I realised the Alarm state goes to servis mode… In config I was using my master code. The Arm at home didn’t work. Any ideas ?

I apologize :pensive:. Just checked again and its working. Strange thing is that I can arm and disarm without actually entering the code even if I have in config:

alarm_control_panel:

  • platform: jablotron
    serial_port: /dev/hidraw0
    code: xxxx
    code_arm_required: true
    code_disarm_required: true

Yes, this looks sensible

The code uses those parameters to decide if to send the code that is entered in the UI to the alarm system. It does not configure the alarm system. I chose to do it this way as I didn’t really want to store my alarm code in HA which would be needed to validate the code.

The way it works at the moment, If you want a code to be required, you configure your alarm system to require the code and then this component.

Would that work for you?

@Marcel1: Ok, I understand. Things are getting complicated, so let’s make things clear.
As far as I can see, we have two kind of topics going on:

Timeline for alarm state

  1. In post 67 I discovered a packet to get the alarm state with a faster response.

  2. You confirmed this in post 75

So far so good. This is the progress we got on the alarm_control_panel platform.

Timeline for sensor state

  1. In post 58, I published my analysis on sensor data, while sniffing USB traffic on a host directly connected to the alarm system while running J/F-link in the background. I saw one 52 07 packet, but in general all sensors report states in a 52 09 packet (I was showing my data in decimal, where dec 82 == hex 52)

  2. In post 60 you confirmed my analysis. You even noticed the 55 08 and 55 09 packets.

We were not able to retreive them without having J/F-link opened, because back then we only had a startup message in Matt’s Jablotron code which showed us the state of the alarm, not the state of the sensors.

  1. In post 78 I discovered a specific startup message we could use in (a separate version of) Matt’s Jablotron code for retreiving sensor states, which won’t require us to have J/F-link opened. The concept of a binary sensor was born, yay \o/

  2. In post 83 you tried to compare your analysis (based on J/F-link data) with my analysis (based on startup message responses). And in post 85 things got blurry for both of us I guess.

So there’s one way to get back on track: publish a new version of my code asap, in order to share and discuss test results. I’ll try to do this tonight.

OK, and what part of the config should I change in order to arm and disarm the alarm from HA only with code entered? I don’t actually mind if the code will be stored somewhere in secret.yaml
Thank you

hi @plaksnor. Thing are getting complicated, but i am still with you entirely.
I am not lost in terms of the steps we have taken so far, also it is entirely clear what our goals is.
It’s more that I am lost in terms of the codes generated (or not generated). I assumed we were on the right track with finding those codes, but now, with the new startup code and the modified _read function, i am not able to capture the codes anymore.

Even when trying to open one session, doing: echo -ne "\x80\x01\x02" > /dev/hidraw0 and opening a second where i do cat /dev/hidraw0 | hexdump -C | grep '55 09' i am not catching any 55 09 anymore.
I really can’t get my head around why this is.

Let me know once you posted your new version.