Jablotron JA-10x over JA-121T with ESP32 (esphome)

Hello, this project is about connecting Jablotron alarm JA-107 over Jablotron RS485 interface JA-121T wirelesly connected to HA over ESPhome (wifi).
You can find other “project” for connecting alarm main unit directly (over USB) to HA. I think, that JA-121T is more secure and wireless, but with limited options. GitHub - kukulich/home-assistant-jablotron100: Home Assistant custom component for JABLOTRON 100+ alar

I share this, because this can help to someone or someone can make this code better.

What is working:

  • Read stale of zones (armed/ready/other…) [text sensor]
  • Set and Unset armed state [two switches]
  • Read PGS [binary sensors]
  • Set PGs [switch] (un/set confirmed by binary sensor)
  • very limited ability to read some periphery state

Hardware:
Please use ESP32 because of hardware UARTs and serial converter TTL (3.3V) to RS485 (from china).

This assume that you have installed ESPhome addon and you have read information about custom components. (Custom Sensor Component — ESPHome)

First you need to put C file to your ESPhome directory /config/ esphome/esp32-alarm

#include "esphome.h"
#include <vector>
#include <sstream>
#include <bitset>

using namespace std;

//please read notes in .yaml file

class JA121T : public Component, public UARTDevice {
 public:
  JA121T(UARTComponent *parent) : UARTDevice(parent) {}
  
  void setup() override {
    // This will be called by App.setup()
  }

  //Sensors
  TextSensor *received_line = new TextSensor();
  TextSensor *version = new TextSensor();
  TextSensor *error = new TextSensor();  
  //PG[0] is not used
  TextSensor * PG[11] = { new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),
    new TextSensor() };
  //STATE[0] is not used
  TextSensor * STATE[11] = { new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),
    new TextSensor() };
  //PRFSTATE[0] is used
  /*  
  TextSensor * PRFSTATE[64] = { new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),
  new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),
  new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),
  new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),
  new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),
  new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor(),  
  new TextSensor(), new TextSensor(), new TextSensor(), new TextSensor() };  
  */

  vector<string> split (const string &s, char delim) {
    vector<string> result;
    stringstream ss (s);
    string item;
    while (getline (ss, item, delim)) {
        result.push_back (item);
    }
    return result;
  }

  int readline(int readch, char *buffer, int len)
  {
    static int pos = 0;
    int rpos;

    if (readch > 0) {
      switch (readch) {
        case '\n': // Ignore new-lines
          break;
        case '\r': // Return on CR
          rpos = pos;
          pos = 0;  // Reset position index ready for next time
          return rpos;
        default:
          if (pos < len-1) {
            buffer[pos++] = readch;
            buffer[pos] = 0;
          }
      }
    }
    // No end of line has been found, so return -1.
    return -1;
  }

  void loop() override {
    const int max_line_length = 80;
    static char buffer[max_line_length];
    while (available()) {
      if(readline(read(), buffer, max_line_length) > 0) {
        // nacteny radek v bufferu (jeden, bez "\n")
        
        string line(buffer); //convert to string
        received_line->publish_state(line);     // publish line
        vector<string> data = split (line+"   ", ' ');
        //ESP_LOGD("JA121T", "Data: '%s' '%s' '%s'", data[0].c_str(), data[1].c_str(), data[2].c_str());
        //Version
        if (data[0].compare("JA-121T,") == 0) {
          ESP_LOGD("JA121T", "Version: %s", line.c_str());
          version->publish_state(line);
        }  
        //OK
        if (data[0].compare("OK") == 0) {
          ESP_LOGD("JA121T", "> OK");
          error->publish_state("0");
        }  
        //ERROR
        if (data[0].compare("ERROR") == 0) {
          ESP_LOGD("JA121T", "> ERROR");
          error->publish_state("1");
        }
        //PGs
        if (data[0].compare("PG") == 0) {
          //ESP_LOGD("JA121T", "> PG");
          int pg=atoi( data[1].c_str() );
          //ESP_LOGD("JA121T", "pg= %i", pg);      
          int size=(sizeof(PG)/sizeof(*PG));
          //ESP_LOGD("JA121T", "size= %i", size);  
          if (pg<size) {          
            //ESP_LOGD("JA121T", "> - %i", pg); 
            ESP_LOGD("JA121T", "> PG %i is %s", pg, data[2].c_str());
            if (data[2].compare("OFF") == 0) {
              PG[pg]->publish_state("0");
            }  
            if (data[2].compare("ON") == 0) {
              PG[pg]->publish_state("1");
            }
          }
          else
          {
            ESP_LOGD("JA121T", "PG %i out of array", pg); 
          }
        }
        //STATE
        if (data[0].compare("STATE") == 0) {
          int state=atoi( data[1].c_str() );
          //ESP_LOGD("JA121T", "state= %i", state);      
          int size=(sizeof(STATE)/sizeof(*STATE));
          //ESP_LOGD("JA121T", "size= %i", size);  
          if (state<size) {  
            //ESP_LOGD("JA121T", "> - %i", state);             
            ESP_LOGD("JA121T", "> STATE %i is %s",state,data[2].c_str());
            STATE[state]->publish_state(data[2].c_str());
            if (data[2].compare("READY") == 0) {                     
            }
            if (data[2].compare("ARMED") == 0) {
            }  
            if (data[2].compare("ARMED_PART") == 0) {
            }  
            if (data[2].compare("MAINTENANCE") == 0) {
            } 
            if (data[2].compare("SERVICE") == 0) {
            }  
            if (data[2].compare("BLOCKED") == 0) {
            }  
            if (data[2].compare("OFF") == 0) {
            }
          }  
          else
          {
            ESP_LOGD("JA121T", "STATE %i out of array", state); 
          }
        }  
        //PRFSTATE
        if (data[0].compare("PRFSTATE") == 0) {
          for (int i = 0; i < 8; i++) { //8x8=64 perif.
            string one="0x"+data[1].substr (i*2,2); //each char of responce from left
            stringstream ss;
            ss << hex << one;
            unsigned n;
            ss >> n;
            bitset<8> b(n);
            ESP_LOGD("JA121T", "> Decoded byte %i: %s",i,b.to_string().c_str());
            /*
            for (int j = 0; j < 8; j++) {
              if (b[j] >0) {
                ESP_LOGD("JA121T", "> Perif %i = 1",(j+(i*8)));
                PRFSTATE[j+(i*8)]->publish_state("1");
              }
              else{
                PRFSTATE[j+(i*8)]->publish_state("0");
              }  
            }
            */
          }  
        }        
      }
    }
  }
};

This code creates component JA121T (with UART defined in .yaml). Commented lines (PRFSTATE) are for periphery state (but when you uncommented all, ESP is not 100% stable - wifi log connection problems).
Receiver line from Jablotron protocol is in “received_line” text sensor (please disable this sensor in logger). When you UN/ARM or change PGs alarm send new line automaticaly and values are decodes and saved in arrays PG[] and STATE[] (index 0 is not used, because of Jablotron terminoligy).
If you want more PGs or Zones make a array larger (code detects array size).

Definiton for ESPhome (.yaml file)

esphome:
  name: esp32-alarm
  platform: ESP32
  board: esp32dev
  includes:
    - esp32-alarm/src/ja121t.h
    
#NOTES:
#this is overkill for esphome (many ensors)
#do not try ESP8266
#if you use (max) 64 pheriphery, you "can" read it by textsensor (commented),
#  but is impossible to convert it to binary sensor like PGs. If you try it ESP don't
#  boot/log to wifi/home assistant/no connection for log
#There is probubly some way to use array in yaml, but I cannot fing informations.

# Enable logging
logger:
  hardware_uart: UART0

# Enable Home Assistant API
api:

ota:
  password: "#####################"

wifi:
  ssid: "#####################"
  password: "#####################"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp32-Alarm Fallback Hotspot"
    password: "#####################"

captive_portal:

globals:
# For PGs switches
  - id: PG001_g
    type: bool
    initial_value: "0"
  - id: PG002_g
    type: bool
    initial_value: "0"
  - id: PG003_g
    type: bool
    initial_value: "0"
  - id: PG004_g
    type: bool
    initial_value: "0"
  - id: PG005_g
    type: bool
    initial_value: "0"
  - id: PG006_g
    type: bool
    initial_value: "0"
  - id: PG007_g
    type: bool
    initial_value: "0"
  - id: PG008_g
    type: bool
    initial_value: "0"
  - id: PG009_g
    type: bool
    initial_value: "0"
  - id: PG010_g
    type: bool
    initial_value: "0"

uart:
  tx_pin: GPIO17 #TX2
  rx_pin: GPIO16 #RX2
  baud_rate: 9600
  id: uart_bus

switch:
  - platform: template
    name: "JA100-Read version"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: 'VER'
  #SET
  - platform: template
    name: "JA100-SET01"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 1'
  - platform: template
    name: "JA100-SET02"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 2'
  - platform: template
    name: "JA100-SET03"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 3'
  - platform: template
    name: "JA100-SET04"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 4'
  - platform: template
    name: "JA100-SET05"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 5'
  - platform: template
    name: "JA100-SET06"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 6'
  - platform: template
    name: "JA100-SET07"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 7'
  - platform: template
    name: "JA100-SET08"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 8'
  - platform: template
    name: "JA100-SET09"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 9'
  - platform: template
    name: "JA100-SET10"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### SET 10'
  #UNSET
  - platform: template
    name: "JA100-UNSET01"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 1'
  - platform: template
    name: "JA100-UNSET02"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 2'
  - platform: template
    name: "JA100-UNSET03"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 3'
  - platform: template
    name: "JA100-UNSET04"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 4'
  - platform: template
    name: "JA100-UNSET05"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 5'
  - platform: template
    name: "JA100-UNSET06"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 6'
  - platform: template
    name: "JA100-UNSET07"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 7'
  - platform: template
    name: "JA100-UNSET08"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 8'
  - platform: template
    name: "JA100-UNSET09"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 9'
  - platform: template
    name: "JA100-UNSET10"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: '###CODE### UNSET 10'    
#Commands  
  - platform: template
    name: "JA100-Read states"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: 'STATE'
  - platform: template
    name: "JA100-Read PGs"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: 'PGSTATE 1 2 3 4 5 6 7 8 9 10' #this (and array in .h) limit number of PGs
  - platform: template
    name: "JA100-Read periphery states"
    icon: mdi:play
    lambda: |-
        return false;
    turn_on_action:
      - uart.write: 'PRFSTATE'
#PG      
  - platform: template
    name: "JA100-PG001"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG001_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 1'
      - globals.set:
          id: PG001_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 1'
      - globals.set:
          id: PG001_g
          value: '0'
  - platform: template
    name: "JA100-PG002"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG002_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 2'
      - globals.set:
          id: PG002_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 2'
      - globals.set:
          id: PG002_g
          value: '0'
  - platform: template
    name: "JA100-PG003"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG003_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 3'
      - globals.set:
          id: PG003_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 3'
      - globals.set:
          id: PG003_g
          value: '0'
  - platform: template
    name: "JA100-PG004"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG004_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 4'
      - globals.set:
          id: PG004_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 4'
      - globals.set:
          id: PG004_g
          value: '0'
  - platform: template
    name: "JA100-PG005"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG005_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 5'
      - globals.set:
          id: PG005_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 5'
      - globals.set:
          id: PG005_g
          value: '0'
  - platform: template
    name: "JA100-PG006"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG006_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 6'
      - globals.set:
          id: PG006_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 6'
      - globals.set:
          id: PG006_g
          value: '0'
  - platform: template
    name: "JA100-PG007"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG007_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 7'
      - globals.set:
          id: PG007_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 7'
      - globals.set:
          id: PG007_g
          value: '0'
  - platform: template
    name: "JA100-PG008"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG008_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 8'
      - globals.set:
          id: PG008_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 8'
      - globals.set:
          id: PG008_g
          value: '0'
  - platform: template
    name: "JA100-PG009"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG009_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 9'
      - globals.set:
          id: PG009_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 9'
      - globals.set:
          id: PG009_g
          value: '0'
  - platform: template
    name: "JA100-PG010"
    icon: mdi:toggle-switch
    lambda: |-
      if (id(PG010_g)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - uart.write: '###CODE### PGON 10'
      - globals.set:
          id: PG010_g
          value: '1'
    turn_off_action:
      - uart.write: '###CODE### PGOFF 10'
      - globals.set:
          id: PG010_g
          value: '0'

text_sensor:
- platform: custom
  lambda: |-
    auto JA121 = new JA121T(id(uart_bus));
    App.register_component(JA121);
    return {JA121->received_line, JA121->version, JA121->error,
      JA121->PG[1],JA121->PG[2],JA121->PG[3],JA121->PG[4],JA121->PG[5],JA121->PG[6],JA121->PG[7],JA121->PG[8],JA121->PG[9],JA121->PG[10],
      JA121->STATE[1],JA121->STATE[2],JA121->STATE[3],JA121->STATE[4],JA121->STATE[5],JA121->STATE[6],JA121->STATE[7],JA121->STATE[8],JA121->STATE[9],JA121->STATE[10]};
#      JA121->PRFSTATE[0],JA121->PRFSTATE[1],JA121->PRFSTATE[2],JA121->PRFSTATE[3],JA121->PRFSTATE[4],JA121->PRFSTATE[5],JA121->PRFSTATE[6],JA121->PRFSTATE[7],JA121->PRFSTATE[8],JA121->PRFSTATE[9],
#      JA121->PRFSTATE[10],JA121->PRFSTATE[11],JA121->PRFSTATE[12],JA121->PRFSTATE[13],JA121->PRFSTATE[14],JA121->PRFSTATE[15],JA121->PRFSTATE[16],JA121->PRFSTATE[17],JA121->PRFSTATE[18],JA121->PRFSTATE[19],
#      JA121->PRFSTATE[20],JA121->PRFSTATE[21],JA121->PRFSTATE[22],JA121->PRFSTATE[23],JA121->PRFSTATE[24],JA121->PRFSTATE[25],JA121->PRFSTATE[26],JA121->PRFSTATE[27],JA121->PRFSTATE[28],JA121->PRFSTATE[29],
#      JA121->PRFSTATE[30],JA121->PRFSTATE[31],JA121->PRFSTATE[32],JA121->PRFSTATE[33],JA121->PRFSTATE[34],JA121->PRFSTATE[35],JA121->PRFSTATE[36],JA121->PRFSTATE[37],JA121->PRFSTATE[38],JA121->PRFSTATE[39],
#      JA121->PRFSTATE[40],JA121->PRFSTATE[41],JA121->PRFSTATE[42],JA121->PRFSTATE[43],JA121->PRFSTATE[44],JA121->PRFSTATE[45],JA121->PRFSTATE[46],JA121->PRFSTATE[47],JA121->PRFSTATE[48],JA121->PRFSTATE[49],
#      JA121->PRFSTATE[50],JA121->PRFSTATE[51],JA121->PRFSTATE[52],JA121->PRFSTATE[53],JA121->PRFSTATE[54],JA121->PRFSTATE[55],JA121->PRFSTATE[56],JA121->PRFSTATE[57],JA121->PRFSTATE[58],JA121->PRFSTATE[59],
#      JA121->PRFSTATE[60],JA121->PRFSTATE[61],JA121->PRFSTATE[62],JA121->PRFSTATE[63]
      
    
  text_sensors:
    - name: "received_line"
      icon: mdi:textlong
    - name: "version"
      icon: mdi:textshort
    - name: "error"
    - id: PG001_i
      on_value:
        - binary_sensor.template.publish:
            id: PG001_t #turn binary sensor
            state: !lambda 'return id(PG001_i).state == "1";'
        - globals.set: #turn switch
            id: PG001_g
            value: !lambda 'return id(PG001_i).state == "1";'
    - id: PG002_i
      on_value:
        - binary_sensor.template.publish:
            id: PG002_t #turn binary sensor
            state: !lambda 'return id(PG002_i).state == "1";'
        - globals.set:
            id: PG002_g #turn switch
            value: !lambda 'return id(PG002_i).state == "1";'
    - id: PG003_i
      on_value:
        - binary_sensor.template.publish:
            id: PG003_t #turn binary sensor
            state: !lambda 'return id(PG003_i).state == "1";'
        - globals.set:
            id: PG003_g #turn switch
            value: !lambda 'return id(PG003_i).state == "1";'
    - id: PG004_i
      on_value:
        - binary_sensor.template.publish:
            id: PG004_t #turn binary sensor
            state: !lambda 'return id(PG004_i).state == "1";'
        - globals.set:
            id: PG004_g #turn switch
            value: !lambda 'return id(PG004_i).state == "1";'
    - id: PG005_i
      on_value:
        - binary_sensor.template.publish:
            id: PG005_t #turn binary sensor
            state: !lambda 'return id(PG005_i).state == "1";'
        - globals.set:
            id: PG005_g #turn switch
            value: !lambda 'return id(PG005_i).state == "1";'
    - id: PG006_i
      on_value:
        - binary_sensor.template.publish:
            id: PG006_t #turn binary sensor
            state: !lambda 'return id(PG006_i).state == "1";'
        - globals.set:
            id: PG006_g #turn switch
            value: !lambda 'return id(PG006_i).state == "1";'
    - id: PG007_i
      on_value:
        - binary_sensor.template.publish:
            id: PG007_t #turn binary sensor
            state: !lambda 'return id(PG007_i).state == "1";'
        - globals.set:
            id: PG007_g #turn switch
            value: !lambda 'return id(PG007_i).state == "1";'            
    - id: PG008_i
      on_value:
        - binary_sensor.template.publish:
            id: PG008_t #turn binary sensor
            state: !lambda 'return id(PG008_i).state == "1";'
        - globals.set:
            id: PG008_g #turn switch
            value: !lambda 'return id(PG008_i).state == "1";'            
    - id: PG009_i
      on_value:
        - binary_sensor.template.publish:
            id: PG009_t #turn binary sensor
            state: !lambda 'return id(PG009_i).state == "1";'
        - globals.set:
            id: PG009_g #turn switch
            value: !lambda 'return id(PG009_i).state == "1";'            
    - id: PG010_i
      on_value:
        - binary_sensor.template.publish:
            id: PG010_t #turn binary sensor
            state: !lambda 'return id(PG010_i).state == "1";'
        - globals.set:
            id: PG010_g #turn switch
            value: !lambda 'return id(PG010_i).state == "1";'
#STATE
    - name: "state01"
      icon: mdi:security
    - name: "state02"
      icon: mdi:security
    - name: "state03"
      icon: mdi:security
    - name: "state04"
      icon: mdi:security
    - name: "state05"
      icon: mdi:security
    - name: "state06"
      icon: mdi:security
    - name: "state07"
      icon: mdi:security
    - name: "state08"
      icon: mdi:security
    - name: "state09"
      icon: mdi:security
    - name: "state10"   
      icon: mdi:security
#PRFSTATE
#    - name: "PRF000"
#    - name: "PRF001"
#    - name: "PRF002"
#    - name: "PRF003"
#    - name: "PRF004"
#    - name: "PRF005"
#    - name: "PRF006"
#    - name: "PRF007"
#    - name: "PRF008"
#    - name: "PRF009"
#    - name: "PRF010"
#    - name: "PRF011"
#    - name: "PRF012"
#    - name: "PRF013"
#    - name: "PRF014"
#    - name: "PRF015"
#    - name: "PRF016"
#    - name: "PRF017"
#    - name: "PRF018"
#    - name: "PRF019"
#    - name: "PRF020"
#    - name: "PRF021"
#    - name: "PRF022"
#    - name: "PRF023"
#    - name: "PRF024"
#    - name: "PRF025"
#    - name: "PRF026"
#    - name: "PRF027"
#    - name: "PRF028"
#    - name: "PRF029"
#    - name: "PRF030"
#    - name: "PRF031"
#    - name: "PRF032"
#    - name: "PRF033"
#    - name: "PRF034"
#    - name: "PRF035"
#    - name: "PRF036"
#    - name: "PRF037"
#    - name: "PRF038"
#    - name: "PRF039"
#    - name: "PRF040"
#    - name: "PRF041"
#    - name: "PRF042"
#    - name: "PRF043"
#    - name: "PRF044"
#    - name: "PRF045"
#    - name: "PRF046"
#    - name: "PRF047"
#    - name: "PRF048"
#    - name: "PRF049"
#    - name: "PRF050"
#    - name: "PRF051"
#    - name: "PRF052"
#    - name: "PRF053"
#    - name: "PRF054"
#    - name: "PRF055"
#    - name: "PRF056"
#    - name: "PRF057"
#    - name: "PRF058"
#    - name: "PRF059"
#    - name: "PRF060"
#    - name: "PRF061"
#    - name: "PRF062"
#    - name: "PRF063"

    
binary_sensor:
#PG
  - platform: template
    id: PG001_t
    name: "PG001"
  - platform: template
    id: PG002_t
    name: "PG002"    
  - platform: template
    id: PG003_t
    name: "PG003"    
  - platform: template    
    id: PG004_t
    name: "PG004"    
  - platform: template    
    id: PG005_t
    name: "PG005"    
  - platform: template    
    id: PG006_t
    name: "PG006"    
  - platform: template    
    id: PG007_t
    name: "PG007"    
  - platform: template    
    id: PG008_t
    name: "PG008"    
  - platform: template    
    id: PG009_t
    name: "PG009"    
  - platform: template    
    id: PG010_t
    name: "PG010"    

On the begining, includes file must point to previous C file “ja121t.h”.
Fill your passwords to “#####################” and set alarm user code to “###CODE###” (with prefix, if you use it). Set limited access for this user in alarm (only some zones and PGs).
I can’t find how to make C file to publish textsensors and binary sensors together, so this sensors are translated in ESPhome.
Global (PG00x_g) are for rememberig PGs switch states when changed form HA (and before are readed back from alarm).
Reading version of JA-121T, “ERROR” and “OK” is useless.
You can malually read zone states and PGs states (if you need more than 10, please edit line near “JA100-Read PGs”).
Use “SET” and “UNSET” switches for ARM/DISARM zomes.
Use “PG” switches and template binary sensors (PG00x_t) for control PGs.
If you chnaged arrays on C file, you need to change mapping on line where component is registered (orded of items have to be same).
Because of some limitations (probubly in ESPhome) is not possible to translate all PRFSTATE (pheriphery) to binary sensors.

You can translate zone states to your language in tempate sensor (configuration.yaml / sensors section)

- platform: template
  sensors:
    alarm_nahore:
      friendly_name: Nahoře
      value_template: >-
        {% if is_state('sensor.state01', 'READY') %}
          Odkódováno
        {% elif is_state('sensor.state01', 'ARMED') %}
          Zakódováno
        {% elif is_state('sensor.state01', 'ARMED_PART') %}
          Zak. částečně
        {% elif is_state('sensor.state01', 'MAINTENANCE') %}
          Údržba
        {% elif is_state('sensor.state01', 'SERVICE') %}
          Seervis
        {% elif is_state('sensor.state01', 'BLOCKED') %}
          Blokováno
        {% elif is_state('sensor.state01', 'OFF') %}
          Nepoužito
        {% else %}
          Neznámý
        {% endif %}

When you turn system on and state of zones is not known, you can use automation to read it: (automations.yaml)

- id: '##########'
  alias: G Alarm Reload States
  description: ''
  trigger:
  - platform: time_pattern
    minutes: /1
  condition:
  - condition: or
    conditions:
    - condition: state
      entity_id: sensor.alarm_nahore
      state: Neznámý
    - condition: state
      entity_id: sensor.alarm_dole
      state: Neznámý
# add more zones
  action:
  - service: switch.turn_on
    target:
      entity_id: switch.ja100_read_states

Here is example of automation whitch waits for confirmed un/lock of zone:

- id: '##########'
  alias: Open garage doors
  description: ''
  trigger:
  - platform: state
    entity_id: binary_sensor.some_remote
    to: 'on'
    from: 'off'
  condition: []
  action:
  - choose:
    - alias: If is locked than unlock and open
      conditions:
      - condition: template
        value_template: '{{ states(''sensor.state01'') != ''READY'' }}'
      sequence:
      - service: switch.turn_on
        target:
          entity_id: switch.ja100_unset01
  - alias: Wait for unlock
    wait_template: '{{ states(''sensor.state01'') == ''READY'' }}'
    timeout: 00:00:10
    continue_on_timeout: true
  - choose:
    - conditions:
      - condition: template
        value_template: '{{ wait.completed }}'
      sequence:
      - service: switch.turn_on
        target:
          entity_id: switch.door_opener
    default:
    - service: .... do something when fails to disarm
  mode: single
2 Likes

Hello,

Incredible ! Thank you for your work. I was starting a project where I looked for Set / Unset Jablotron Sections.
I found the serial adaptator and I started to think about the integration.

I am waiting for my Jablotron to be installed… I will try and work with you project. I will try to add ideas and returns on your code.

Can you give detailed list and wiring of item needed ?

Thanks a lot.

Today I wrote code to decode PRFSTATE line from JA-121T / ESP. With this you can have motion information from sensors/periphery. This is evaluated in HA, not ESP.
Warning: wireless motion sensors are NOT good for light trigering, but can be usable for occupancy.

First, we need to have separated data from PRFSTATE, witch hold value.

- platform: template
  sensors:
    alarm_prfdata:
      friendly_name: Alarm PRF data
      value_template: >-
        {% set line = states('sensor.received_line') %}
        {% if ('PRFSTATE' in line) %}
          {% set data = line.split(' ')[1][0:16] %}
        {% else %}
          {% set data = states('sensor.alarm_prfdata') %}
        {% endif %}
        {{ data }}

Warning: this sensor extracts only 64 devices (addresses from Jablotron), if you want more, change [0:16] to [0:more]

Now you need to make binary sensor, witch extract his bit from above.

- platform: template
  sensors:
    alarm_prf010:
      friendly_name: P10
      value_template: >-
        {% set n = 10 %}
        {% set b = n//8 %}
        {% set p = n%8 %}
        {% set chr = states("sensor.alarm_prfdata")[2*b:2*b+2] %}
        {{ chr | int(base=16,default=0) | bitwise_and(2**p) > 0}}

This make sensor for periphery 10. If you want another, change “N = 10” to different number (address of pheriphery).