How to read a variable from a custom_component (wiegand)

Hi all!
I got stucked with my first custom integration.
I want to integrate a rfid reader.

I have use the code from gdoerr
The code has been successfully read with my rfid reader and printed in the ESPhome logger

Now I have two choices.
1. I can publish the code to my “controlAccess” topic which has a script to check the access permission of the code. But my problem is I need 3 variables to complete the logic proccess:
payload= {
‘id’: ‘door’,
‘code’: wiegand_code,
‘timeStamp’: now()
}
and I dont know if would be possible to build in my esphome.yalm this structure and publish the json directly from the device in to the topic.

2. I could call a script which process the code and build the complete payload.
My problem is that the the script is not tiggered with this config and I dont know why.
My ESPhome yaml is:

custom_component:
  - lambda: |-
       auto wiegand = new WiegandReader(21, 23, "python_scripts.door_mqtt_publish.py");
       return {wiegand};

in config/python_scripts/door_publish_mqtt.py I have a simple code just to test it

logger.info("PYTHON SCRIPT CALLED")
name = data.get('code')
logger.info("HELLOOOOOOOOO %s", name)
hass.bus.fire(name, {"test": "from a Python script!"}
)

and I think that services is not mandatory, anaway:
config/python_scripts/services.yaml

door_mqtt_publish:
  description: publish a rfid code
  field:
    code:
      description: code format
      example: 9451293

In the ESP home logger I can see that the code is read perfectly but I can not trigger the python script to send the payload with the code on it. After read a code nothing appears in the HA logger.

HA logger:

2020-02-03 12:25:05 WARNING (MainThread) [homeassistant.loader] You are using a custom integration for hacs which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant.
2020-02-03 12:25:05 WARNING (MainThread) [homeassistant.loader] You are using a custom integration for customizer which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant.
2020-02-03 12:25:36 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection.1908784496] Error handling message: Unauthorized

1 Like

I have wrote the code and I will check tomorrow to fix the mistakes.
The only thing that I dind´t undertand is how can I add the timeStamp using the api time from esphome.h to my payload setup

Here is where I generate the payload

        void json_message(std::string keyCode) {
            // publish JSON using lambda syntax
            publish_json("the/other/json/topic", [=](JsonObject &root2) {
                root2["door"] = 1
                root2["code"] = keyCode.c_str();
                root2["timeStamp"] = "blabla"
            });
        }

and here the wiegand.h

#include "esphome.h"

/**
 * Wiegand Reader Custom Device
 *
 * Inspired in https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino
 * and https://github.com/esphome/feature-requests/issues/211
*/

class WiegandReader : public PollingComponent, public CustomMQTTDevice {

    public:
        WiegandReader(int pinD0, int pinD1)
            : PollingComponent(200),
            pinD0(pinD0), pinD1(pinD1)

        /**
         * Initial setup
         */
        void setup() override {
            _lastWiegand = 0;
            _cardTempHigh = 0;
            _cardTemp = 0;
            _code = 0;
            _wiegandType = 0;
            _bitCount = 0;
            subscribe("the/topic", &WiegandReader::on_message);

            // Configure the input pins
            pinMode(pinD0, INPUT);
            pinMode(pinD1, INPUT);
            
            // Attach the interrupts
            attachInterrupt(digitalPinToInterrupt(pinD0), ReadD0, FALLING);  // Hardware interrupt - high to low pulse
            attachInterrupt(digitalPinToInterrupt(pinD1), ReadD1, FALLING);  // Hardware interrupt - high to low pulse
        }

        void update() override {
            // See if we have a valid code
            noInterrupts();
            bool rc = DoWiegandConversion();
            interrupts();

            if(rc) {
                // Capture the last time we received a code
                lastCode = millis();
            } else {
                if(keyCodes.length() > 0) {
                    // We have a keyCode, see if the interdigit timer expired
                    if(millis() - lastCode > 2000) {
                        // The interdigit timer expired, send the code and reset for the next string
                        json_message(keyCodes);
                        keyCodes = "";
                    }
                }
            }
        }

    private:
        static volatile unsigned long _cardTempHigh;
        static volatile unsigned long _cardTemp;
        static volatile unsigned long _lastWiegand;
        static volatile int _bitCount;
        static int _wiegandType;
        static unsigned long _code;

        unsigned long lastCode = 0;
        std::string keyCodes = "";

        int pinD0;
        int pinD1;
        std::string serviceName;

        /**
         * Calls a Home Assistant service with the key code
         * @param keyCode lo queremos cambiar por enviar un json
         
        void callHAService(std::string keyCode) {
            call_homeassistant_service(serviceName.c_str(), {
                    {"code", keyCode.c_str()}
            });
        }
        */

        void json_message(std::string keyCode) {
            // publish JSON using lambda syntax
            publish_json("the/other/json/topic", [=](JsonObject &root2) {
                root2["door"] = 1
                root2["code"] = keyCode.c_str();
                root2["timeStamp"] = "blabla"
            });
        }

        /**
         * D0 Interrupt Handler
         */
        static void ReadD0() {
            _bitCount++;				// Increment bit count for Interrupt connected to D0
            if(_bitCount > 31) { 		// If bit count more than 31, process high bits
                _cardTempHigh |= ((0x80000000 & _cardTemp)>>31);	//	shift value to high bits
                _cardTempHigh <<= 1;
                _cardTemp <<=1;
            } else
                _cardTemp <<= 1;		// D0 represent binary 0, so just left shift card data

            _lastWiegand = millis();	// Keep track of last wiegand bit received
        }

        /**
         * D1 Interrupt Handler
         */
        static void ReadD1() {
            _bitCount ++;				// Increment bit count for Interrupt connected to D1

            if(_bitCount > 31) {		// If bit count more than 31, process high bits
                _cardTempHigh |= ((0x80000000 & _cardTemp)>>31);	// shift value to high bits
                _cardTempHigh <<= 1;
                _cardTemp |= 1;
                _cardTemp <<=1;
            } else {
                _cardTemp |= 1;			// D1 represent binary 1, so OR card data with 1 then
                _cardTemp <<= 1;		// left shift card data
            }
            _lastWiegand = millis();	// Keep track of last wiegand bit received
        }

        /**
         * Extract the Card ID from the received bit stream
         * @param codehigh
         * @param codelow
         * @param bitlength
         * @return
         */
        unsigned long getCardId(volatile unsigned long *codehigh, volatile unsigned long *codelow, char bitlength) {
            if (bitlength==26)								// EM tag
                return (*codelow & 0x1FFFFFE) >>1;

            if (bitlength==34)								// Mifare
            {
                *codehigh = *codehigh & 0x03;				// only need the 2 LSB of the codehigh
                *codehigh <<= 30;							// shift 2 LSB to MSB
                *codelow >>=1;
                return *codehigh | *codelow;
            }
            return *codelow;								// EM tag or Mifare without parity bits
        }

        /**
         * Convert the received bitstream
         * @return
         */
        bool DoWiegandConversion () {
            unsigned long cardID;
            unsigned long sysTick = millis();

            if ((sysTick - _lastWiegand) > 25)								// if no more signal coming through after 25ms
            {
                if ((_bitCount==24) || (_bitCount==26) || (_bitCount==32) || (_bitCount==34) || (_bitCount==8) || (_bitCount==4)) { 	// bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34
                    _cardTemp >>= 1;			// shift right 1 bit to get back the real value - interrupt done 1 left shift in advance
                    // wiegand 26 or wiegand 34
                    cardID = getCardId (&_cardTempHigh, &_cardTemp, _bitCount);
                    _wiegandType=_bitCount;
                    _bitCount=0;
                    _cardTemp=0;
                    _cardTempHigh=0;
                    _code=cardID;
                    return true;
                    }
                } else {
                    // well time over 25 ms and bitCount !=8 , !=26, !=34 , must be noise or nothing then.
                    _lastWiegand=sysTick;
                    _bitCount=0;
                    _cardTemp=0;
                    _cardTempHigh=0;
                    return false;
                }
            } else
                return false;
        }
    };

    volatile unsigned long WiegandReader::_cardTempHigh = 0;
    volatile unsigned long WiegandReader::_cardTemp = 0;
    volatile unsigned long WiegandReader::_lastWiegand = 0;
    volatile int WiegandReader::_bitCount = 0;
    unsigned long WiegandReader::_code = 0;
    int WiegandReader::_wiegandType = 0;

Did you solve your issue? I’m looking to integrate my Wiegand Keypad to HA too.
I’m not a big fan of MQTT but i could do it with this for this project…

Yes now is working for my case where I dont use the keypad, but I have to update the code

Can you tell me more about what you did?

I will upload to github and share here I just need a bit of free time.

2 Likes

@Stepan Sorry to be a bug, Just curious if you might have posted your code to GitHub?
I am trying to get custom UART and MQTT functioning, and would appreciate your code as a reference to get an old Matrix amp a new life :slight_smile:


here is my code

I didnt get to pass the variable so I did a bad solution mqtt publish function inside of the custom.h.

Ok so this component send the read code to mqtt message, right? Where must be set the topic?

I’ve got errors when compiling your custom component…

In file included from src/main.cpp:15:0:
src/wiegand_device.h: In member function 'void WiegandReader::json_message(std::string)':
src/wiegand_device.h:89:13: error: 'RealTimeClock' was not declared in this scope
             RealTimeClock *x = new RealTimeClock();
             ^
src/wiegand_device.h:89:28: error: 'x' was not declared in this scope
             RealTimeClock *x = new RealTimeClock();
                            ^
src/wiegand_device.h:89:36: error: expected type-specifier before 'RealTimeClock'
             RealTimeClock *x = new RealTimeClock();
                                    ^
src/wiegand_device.h:89:36: error: expected ';' before 'RealTimeClock'
src/wiegand_device.h:90:13: error: 'ESPTime' was not declared in this scope
             ESPTime time = x->utcnow();
             ^
src/wiegand_device.h:90:21: error: expected ';' before 'time'
             ESPTime time = x->utcnow();
                     ^
src/wiegand_device.h:92:18: error: request for member 'strftime' in 'time', which is of non-class type 'time_t(time_t*) {aka long int(long int*)}'
             time.strftime(time2, 20, "%Y-%m-%d %H:%M:%S");
                  ^
src/wiegand_device.h: In member function 'void WiegandReader::json_message2(long unsigned int)':
src/wiegand_device.h:102:13: error: 'RealTimeClock' was not declared in this scope
             RealTimeClock *x = new RealTimeClock();
             ^
src/wiegand_device.h:102:28: error: 'x' was not declared in this scope
             RealTimeClock *x = new RealTimeClock();
                            ^
src/wiegand_device.h:102:36: error: expected type-specifier before 'RealTimeClock'
             RealTimeClock *x = new RealTimeClock();
                                    ^
src/wiegand_device.h:102:36: error: expected ';' before 'RealTimeClock'
src/wiegand_device.h:103:13: error: 'ESPTime' was not declared in this scope
             ESPTime time = x->utcnow();
             ^
src/wiegand_device.h:103:21: error: expected ';' before 'time'
             ESPTime time = x->utcnow();
                     ^
src/wiegand_device.h:105:18: error: request for member 'strftime' in 'time', which is of non-class type 'time_t(time_t*) {aka long int(long int*)}'
             time.strftime(time2, 20, "%Y-%m-%d %H:%M:%S");
                  ^
*** [/data/wemos_d1_wiegand/.pioenvs/wemos_d1_wiegand/src/main.cpp.o] Error 1
========================= [FAILED] Took 13.84 seconds =========================

I updated the code with the instructions.
The error is because you are missing this part in the .yaml because I forgot to added.
time:
#- platform: homeassistant

Ok, now it is compiled without errors, i will try this evening.
Two questions:

  1. I placed wiegand_device.H in /config/esphome/ and it works. Is it not right?
  2. How work this component? When tag is recognised an mqtt with code is sent to HA, right?
  1. I placed wiegand_device.H in /config/esphome/ and it works. Is it not right?
  2. How work this component? When tag is recognised an mqtt with code is sent to HA, right?
  1. Yes in this folder also work but if you have more than one door should be in src.
  2. This component just publish a code in a topic, then you should create a automation which should check if the code exist in your list of know codes, then I check the timeStamp because a code can arrive with delay and open the door in a conflictive moment of time.

To create this component you can generate a Script, you can also implement it in nodered or others methods. Maybe I have a old code in nodered that can inspired you but I can´t share my actual setup because it is a specific case of use.

If you have just a few keys maybe you can simplify the flux doing the comprobation in the sensor and the sensor can send the message (“open the door”) directly to the actuator.
For security you should have both (actuator and sensor) separately because the sensor normaly its outdoor so accesible.

Ok, thank you. I already have an actuator inside my house and managed in HA. I think i will use an automation to parse the readed code with a list and if it pass drive the actuator (actually i drive it using rest commands from my phone with an app that read an nfc tag, see my signature). Do you use an automation in HA actually to manage readed codes?

I have the sensors/actuators in HA and the logic and DB is in Azure. I use MQTT to transfer info between themselves.

Ok, can you show me how appear the complete mqtt message sent by the reader? I don’t use autodiscovery in ha, so i’m not sure how to setup mqtt sensor to retrieve the code…

The message is JSON format and you can check it easily in developer tools, in mqtt part and then subscribe to your topic.
{
“doorId”: 2,
“code”: 9999999,
“timeStamp”: “xx-yy-zz 00:00:00”
}

Hi, i can’t get it working… Compiling was successfully, but when my esp boot it fail.
I’m using a Wemos D1 mini.
THis is the log in esphome-flasher.

Using 'COM5' as serial port.
Connecting....
Detecting chip type... ESP8266
Connecting...94 %)Writing at 0x00040000... (100 %)Wrote 402176 bytes (276552 compressed) at 0x00000000 in 6.2 seconds (effective 517.1 kbit/s)...
Hash of data verified.

Leaving...
Hard Resetting...
Done! Flashing is complete!

Showing logs:
[19:36:58]$$ll g[I][logger:156]: Log initialized
[19:36:58][C][ota:364]: There have been 3 suspected unsuccessful boot attempts.
[19:36:58][I][app:028]: Running through setup()...
[19:36:58]ISR not in IRAM!
[19:37:01]
[19:37:01]Abort called
[19:37:01]
[19:37:01]>>>stack>>>
[19:37:01]
[19:37:01]ctx: cont
[19:37:01]sp: 3ffffc90 end: 3fffffc0 offset: 01b0
[19:37:01]3ffffe40:  0000001b 3fff21dc 3fff21e8 4020a774  
[19:37:01]3ffffe50:  3fff24f4 00000003 3fff21cc 00000004  
[19:37:01]3ffffe60:  3fff24a4 3fff24a4 00000000 40100776  
[19:37:01]3ffffe70:  3fff24d4 3fff0b84 3fff1e88 4020c4ac  
[19:37:01]3ffffe80:  3fff21e8 00000001 3fff24a4 4020b4f4  
[19:37:01]3ffffe90:  3fff0b48 00000296 00000296 40219a10  
[19:37:01]3ffffea0:  3ffea6d0 3fff24f4 3fff06ec 40219e34  
[19:37:01]3ffffeb0:  3fff24f4 00000001 3fff06ec 4021aa3d  
[19:37:01]3ffffec0:  3fff18ac 00000000 3fff06ec 4020b29d  
[19:37:01]3ffffed0:  3fff24f4 00000007 3fff1e6c 402263ee  
[19:37:01]3ffffee0:  3fff0760 00000000 3fffff68 4020c5f6  
[19:37:01]3ffffef0:  00000000 00000000 3fff23ec 3fff1e6c  
[19:37:01]3fffff00:  3fff0760 3fff06ec 3fff23ec 4020cfac  
[19:37:01]3fffff10:  3fff1d60 00000000 00000000 3fff1d80  
[19:37:01]3fffff20:  00000000 00000000 00000000 4021bd68  
[19:37:01]3fffff30:  00000000 4021bd68 00000000 4021bd68  
[19:37:01]3fffff40:  00000000 4021bd68 00000000 4021bd68  
[19:37:01]3fffff50:  00000000 00000000 3fff1c2c 3fff0b84  
[19:37:01]3fffff60:  4020c5c8 4020c6e0 3fff24ec 3fff24f0  
[19:37:01]3fffff70:  3fff24f0 3fff2420 feefeffe feefeffe  
[19:37:01]3fffff80:  3fff0b84 feefeffe feefeffe feefeffe  
[19:37:01]3fffff90:  feefeffe feefeffe feefeffe 3fff09c0  
[19:37:01]3fffffa0:  3fffdad0 00000000 3fff0990 40218330  
[19:37:01]3fffffb0:  feefeffe feefeffe 3ffe85cc 40100a61  
[19:37:01]<<<stack<<<
[19:37:01]
[19:37:01] ets Jan  8 2013,rst cause:2, boot mode:(3,6)
[19:37:01]
[19:37:01]load 0x4010f000, len 1384, room 16 
[19:37:01]tail 8
[19:37:01]chksum 0x2d
[19:37:01]csum 0x2d
[19:37:01]v8b899c12
[19:37:01]~ld
[19:37:01][I][logger:156]: Log initialized
[19:37:01][C][ota:364]: There have been 4 suspected unsuccessful boot attempts.
[19:37:01][I][app:028]: Running through setup()...
[19:37:01]ISR not in IRAM!
[19:37:05]
[19:37:05]Abort called
[19:37:05]
[19:37:05]>>>stack>>>
[19:37:05]

It looks like you have a problems with the pins. if you have a ESP32 also try the code to discard other kind of problems.