Tuesday, January 13, 2015

Using whatsapp for home automation - The parser

Last time we saw how to install Yowsup on our Raspberry Pi and use the python script yowsup-cli to send and receive messages.

Now it's time to use it for something useful.

With the new Yowsup 2 you cannot use yowsup-cli to send and receive the messages. This is both a good and a bad news. The bad part is that you need to create the classes as described in the author's site, or to modify the existing demos.
The good part is that we will have much more control over the whole system we are creating and everything will be integrated in the script, without the need to execute something external.

To make it easier for my readers, I created two files (modifying the original Yowsup demos) with the needed classes for sending and for receiving Whatsapp messages via Yowsup. You can download them here:

http://www.mascal.it/public/wasend.py
http://www.mascal.it/public/wareceive.py

To use Whatsapp for home automation we need to create a parser. There are many ways to create such task and some are smarter and more powerful than others. We will see just a simple one.

Before everything, let's put these functions to make things easier:

def credential():
    return "391122334455","whatsapp_password"

def Answer(risp):
    try:
        stack=YowsupSendStack(credential(), ["390000000000", risp])
        stack.start()
    return

The first function is used to return your RPi phone number and the password for Whatsapp, so there is no need to read the configuration file (just get the two strings from that file). The second function will send a text to the specified number using the Yowsup classes. Let's go on with the parser...

First of all an infinite loop that will contain our parser (note that at the top of the script there must be the header with the all the declarations that I will skip to concentrate only on the main parsing cycle):

while True:

Than let's check for a received message:

    try:
        stack=YowsupReceiveStack(credential())
        stack.start()
    except MessageReceived as rcvd:

The receiving class will execute and terminate after a few seconds if nothing has been received. In case of a message incoming, it raises a MessageReceived exception.
When this happens, we can just read the value of the exception that will contain what we are looking for. To make it easier to create the parser let's convert the whole string to lowercase:

        received=rcvd.value.lower()

Now let's check if the message has been received by our phone number and not from someone else:

        if received[:len("390000000000")]=="390000000000":
            received=received[len("390000000000"):]

If the number is correct I will strip it off from the string to have just the message. In case the number is not the right one we could append to a log file for inspection. At the end of this post there is a full listing of the script, with the logging statements.

Now we can start parsing the message. We can just search for specific text inside the message and act consequently. Let's start with a simple test that will answer "Hello" when receiving "Hello":

            if received[:5]=="hello": Answer("Hello!")

With this statements we are checking if the received message starts with "hello". Note that case is ignored because we converted everything to lowercase and also note that we could write whatever we like after "hello". We could have sent something like "hello my raspberry pi" and the message would have been correctly interpreted.
Ok, now let's try to do something a bit more useful. Shouldn't be nice if we could update our RPi with just a message? Just add this line after the "hello" test:

            elif received[:6]=="update":
                Answer("Updating...")
                os.system("sudo apt-get -y update")
                Answer("System updated!")

Here we check for the "update" text at the beginning of the received message. If found RPi answers with the text "Updating...", it performs an update without asking for confirmations and at the end it informs us that the update has been completed.

To keep the parser clean, it would be advisable to use functions. So before the main loop we could have something like

def UpdateFunc():
    Answer("Updating...")
    os.system("sudo apt-get -y update")
    Answer("System updated!")
    return

and the parser would become like this:

            elif received[:6]=="update": UpdateFunc()

What if we wish to execute something by different words? For example we could wish to make the RPi restart by sending "restart" or by sending "reboot". Actually that's quite easy:

            elif received[:7]=="restart" or received[:6]=="reboot": RestartFunc()

and we will have the following function declared:

def RestartFunc():
    Answer("Restarting the system...")
    os.system("sudo reboot")
    return

Now we try to manage a message where the expected word could not be at the beginning of the text. As an example we could ask fot the RPi internal temp with messages like these:

  • Temperature?
  • What is your temperature?
  • Is your temperature too high?

and so on. To accomplish this task we can just check if the word "temperature" is inside the message with this test:

            elif "temperature" in received: TempFunc()

and the function would be:

def TempFunc():
    t=float(subprocess.check_output(["/opt/vc/bin/vcgencmd measure_temp | cut -c6-9"], shell=True)[:-1])
    ts=str(t)
    Answer("My temperature is "+ts+" °C")
    return

The word could be everywhere inside the message and the function will be executed anyway.

At the end of the parser there should be something to inform us about unrecognized statements, so we can put a statement like this:

            else: Answer("Sorry, I cannot understand what you are asking for...")

This ends the first part about using Whatsapp for home automation. We saw how to make a simple parser to recognize words (or small sentences) and execute some task according to these words. Actually there is still no real home automation, but we will see something more complex on next post.
Below You can see the Python script with all the above sections:

import os, subprocess, yowsup, logging
from wasend import YowsupSendStack
from wareceive import YowsupReceiveStack, MessageReceived

def credential():
    return "391122334455","whatsapp_password"

def Answer(risp):
    try:
        stack=YowsupSendStack(credential(), ["390000000000", risp])
        stack.start()
    except:
        pass
    return

def UpdateFunc():
    Answer("Updating...")
    os.system("sudo apt-get -y update")
    Answer("System updated!")
    return

def RestartFunc():
    Answer("Restarting the system...")
    os.system("sudo reboot")
    return

def TempFunc():
    t=float(subprocess.check_output(["/opt/vc/bin/vcgencmd measure_temp | cut -c6-9"], shell=True)[:-1])
    ts=str(t)
    Answer("My temperature is "+ts+" °C")
    return

while True:
    try:
        stack=YowsupReceiveStack(credential())
        stack.start()
    except MessageReceived as rcvd:
        received=rcvd.value.lower()
        if received[:len("390000000000")]=="390000000000":
            received=received[len("390000000000"):]
            if received[:5]=="hello": Answer("Hello!")
            elif received[:6]=="update": UpdateFunc()
            elif received[:7]=="restart" or received[:6]=="reboot": RestartFunc()
            elif "temperature" in received: TempFunc()
            else: Answer("Sorry, I cannot understand what you are asking for...")
        else: #message from wrong sender
            with open("/home/pi/whatsapp.log","a") as mf: mf.write("Wrong sender: "+received[:len("390000000000")]+"\n")

101 comments:

  1. Did I install wrong? I can not find out the directory "src" as you mentioned in modifying the file ListenerClient.py.
    When I run ls command, it show below files and dirs:
    "build config dist LICENSE README.md setup.py yowsup yowsup2.egg-info yowsup-cli "

    ReplyDelete
  2. Yowsup has been updated few days ago and it seems that several changes has been implemented.
    I'm going to check this and I will put an updated guide for this version as soon as possible.

    ReplyDelete
  3. Thank you for quick response. Hope that you will fix it soon. I want to use rpi as a gate keeper and with a pi camera, it can send me an image every time there is a man in front of my house.

    ReplyDelete
  4. The nice thing is that reading the new features it seems that you can send and receive images with yowsup-cli without the need to modify the source code.
    I will try to update the guide in these days.

    ReplyDelete
  5. Updated for Yowsup 2! You should also take a look at the previous post (or at the Yowsup main site) for a correct installation.

    ReplyDelete
  6. Hello friend, okay?
    Your article is very good and I saw that can do what I'm trying to days.
    I need to open the connection to yowsup order to get the status of the messages received, but my knowledge in python is not good about to rewrite things. Thus, I'm trying directly by the shell using a named pipe. Infezlimente, when sending the first message through the pipe, this error happened.

    "Exception in thread Thread-1:
    Traceback (most recent call last):
    File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
    File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
    File "/usr/local/src/yowsup/yowsup/demos/cli/cli.py", line 150, in startInputThread
    cmd = self._queuedCmds.pop(0) if len(self._queuedCmds) else input(self.getPrompt()).strip()
    EOFError: EOF when reading a line"


    You have no idea how to help me?
    thank you very much!

    ReplyDelete
    Replies
    1. Hi, unfortunately I never saw this error.

      Anyway it seems cli is expecting to find some input text, but find nothing. Maybe the message is not arriving to cli.py through your pipe... How did you invoke yowsup in the pipe?

      Delete
  7. I found out this script is kind of easy to crash.
    Traceback (most recent call last):
    File "whatsapp_parser_from_me.py", line 43, in
    stack.start()
    File "/home/pi/parser/wareceive.py", line 67, in start
    self.stack.loop(count=4)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/stacks/yowstack.py", line 170, in loop
    asyncore.loop(*args, **kwargs)
    File "/usr/lib/python2.7/asyncore.py", line 220, in loop
    poll_fun(timeout, map)
    File "/usr/lib/python2.7/asyncore.py", line 156, in poll
    read(obj)
    File "/usr/lib/python2.7/asyncore.py", line 83, in read
    obj.handle_read_event()
    File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
    self.handle_read()
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/network/layer.py", line 48, in handle_read
    self.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/network/layer.py", line 55, in receive
    self.toUpper(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/__init__.py", line 55, in toUpper
    self.__upper.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/stanzaregulator/layer.py", line 28, in receive
    self.processReceived()
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/stanzaregulator/layer.py", line 48, in processReceived
    self.toUpper(oneMessageData)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/__init__.py", line 55, in toUpper
    self.__upper.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/auth/layer_crypt.py", line 63, in receive
    self.toUpper(payload)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/__init__.py", line 55, in toUpper
    self.__upper.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/coder/layer.py", line 35, in receive
    self.toUpper(node)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/__init__.py", line 55, in toUpper
    self.__upper.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/__init__.py", line 160, in receive
    s.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/__init__.py", line 101, in receive
    recv(node)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/protocol_acks/layer.py", line 17, in recvAckNode
    self.toUpper(IncomingAckProtocolEntity.fromProtocolTreeNode(node))
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/__init__.py", line 55, in toUpper
    self.__upper.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.2.78-py2.7.egg/yowsup/layers/interface/interface.py", line 71, in receive
    self.callbacks[entityType](entity)
    File "/home/pi/parser/wasend.py", line 43, in onAck
    raise KeyboardInterrupt()

    Above occasionally happens. I dont know the reason.
    Do I have to study yowsup to solve this issue?

    ReplyDelete
    Replies
    1. Hi, did you resolve it please somehow? I found the parser.py script a bit unstable. Mainly due to that wasend.py keyboard interception.

      Let me please know if you have any solution for it

      Delete
  8. Actually, the script wasend.py uses the keyboardinterrupt exception to return to the parser. I agree that this is not the best way, but they are just examples...
    You should catch the exception in the parser if you whant to avoid the problem (the try...except statement could even do nothing after catching the exception, but it needs to exist).

    If you wish, you can just modify the wasend.py to exit differently from the python class.

    Anyway I wish to modify the scripts by myself to make them a bit better and probably I will upload the new ones when ready.

    ReplyDelete
    Replies
    1. Carlo please can you modify the wasend.py in the way that it will not crash? i am not a programmer but I would like to use you perfect idea to send commands to raspi.

      thanks

      Delete
    2. Lately I've been quite busy with other thing, sorry. Anyway Im' testing a new version of that script that should work better.
      As soon as I finish all the testing I promise that I will make it available and I will change the parser above to use the new wasend (actually a really small change)!

      Delete
    3. Finally I managed to complete the tests. Now the new wasend.py is downloadable (from the link in the post) and I also changed the example script above (just the Answer function has been changed a little).

      The new script exits logging out from Whatsapp instead od raising an exception that should be catched. This way works much better!

      Delete
  9. Thank you for your advice.
    Actually using whatsapp instead of email or ssh to control raspi is even more convenient than I thought.
    Thank you for your dedication.
    You are genius.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  10. I know it might be inappropriate to ask here.
    But I wonder how should I start learning about yowsup as I really enjoy using its function.
    I have learned python for several months and ready for something real(such as creating a custom yowsup layer (like what you did),
    but I still found yowsup tedious to start with. At least I want to create my own version of yowsup layer.
    Any suggestion?

    ReplyDelete
    Replies
    1. Unfortunately Yowsup is almost without documentation, so the only way to learn how it works is looking at the demos (I did this way).
      Also looking at the issues in the git page where you download Yowsup could help ( https://github.com/tgalal/yowsup/issues?q=is%3Aissue ).

      Yes, it's quite tedious learning it this way (it took several month to me to make it works), but I do not think there are other choices...

      Delete
    2. wow several MONTHS, that require tons of patience and passion.
      Thank you for providing me this invaluable suggestion.
      I assume this is a "must" path for anyone who wants to be a user or contributor.
      I found it is kind of cool to build up a custom layer, I hope I can one day utilize yowsup and raspi to deploy some kind of home automation smoothly.
      May be even build up a graphic interface, let my family members can monitor their home condition by just asking raspi through whatsapp

      Delete
    3. Anyway remember that I was also a Python "novice", so maybe it could take less for you. Moreover several months to have a complete Yowsup manager (so also sending images). Results came much earlier...

      The project you are thinking of is almost the same I wish to build and the main reason I started this blog! Actually it's constantly evolving, so changes happens quite often (soon you will see a post about my last "upgrade").
      If you need help, I hope my blog (and me) can provide some help. Suggestions (and corrections) are of course always welcome!

      Delete
    4. Hey I have found something cool.
      Someone has the same idea!
      https://github.com/tgalal/yowsup/issues/647

      Delete
  11. Sir,
    In this example using what's app to run a command in raspberry pi, the above like update reboot??

    ReplyDelete
  12. NameError: name 'received' is not defined

    help

    ReplyDelete
    Replies
    1. Are you sure tha you copied all the parts? In the mani loop I assign a value to that var:
      received=rcvd.value.lower()
      If you missed that line "received" cannot exists and the script will give such error...

      Delete
    2. Sir,
      This script is exist, and the error should come. How to solve this.

      Replay me
      Thanks

      Delete
    3. If that row is present I really do not know. Maybe indentation not correct? Anyway it's quite difficult without looking at that part of the script...

      Delete
  13. Any another way to define 'received' ?

    Thanks

    ReplyDelete
    Replies
    1. "received" contains the received message. It will be defined getting the text from the MessageReceived exception that will be raised by the stack when a message arrives to the RPi.
      If the part of the script that runs the stack and manages the exception is in your script, it's really impossible to find the problem without looking at it.

      Delete
  14. Ok

    Thanks for your replays,
    This blog is very useful to me.

    Thanks.

    ReplyDelete
  15. i love u, thanks for the info
    by from argentina

    ReplyDelete
  16. Joop Kuijntjes02 June, 2015 14:39

    I do get the same error as: Anonymous15 March, 2015 10:46

    I do get a Whatsapp message back, but always as a answer on my very first message send from my mobile. So it looks like, they are not deleted.

    ReplyDelete
    Replies
    1. Joop Kuijntjes02 June, 2015 16:59

      After a couple of times testing, is it working correct.

      Delete
    2. Glad to know that now it's working!

      Delete
  17. Dear Carlo, yesterday you already helped me first time in this case - many thanks. Everything like mentioned in your post "WhatsApp Raspberry Pi / Yowsup" works exactly with my Pi - perfect!
    Today I am here with another big problem for me:
    Obviously it works while I'm using the RPI headless via a ssh-session. But if I end this session (I want to run the RPI totally headless), the listener obviously don't work anymore.
    My goal is to execute a file with autostart of Rpi which listens permanently for received whatsapp-messages and reacts depending of the content of the received message. I try to understand this page but I really have no idea what to. In which file(type) to I have to put which code?
    Maybe you are patient enough to help me again... Again many thanks for your very patient and professional cooperation with readers of your blog! Dirk

    ReplyDelete
    Replies
    1. Starting a script at power on is not too difficult, but a bit long to write in a comment. Fortunately I'm going to write a new post about this topic, so soon you can find what you are asking for!

      Delete
    2. ...perfect! I will wait for that stuff, many thanks!

      Delete
  18. I published it few hours ago. Take a look at the last post!

    ReplyDelete
    Replies
    1. Hi, sorry for my bad programmimg skills...
      I installed the yousup stuff -> ok
      - learned how to make a *.py file ("testing.py" e.g.) automatic running with every new (re)boot (your post July 2015) -> ok.
      - copied the two files "Wasend.py" & "wareceiver.py" mentioned above in the path "/home/pi" -> ok

      To be honest - I did not understand, in which file I have to put the "Qestion and Answer"-code mentiones above... may you help me again? many thanks again

      Delete
  19. Hi, sorry for asking again, but I don't know how to run this stuff....What have I done:
    - Copied the file "wasend.py" to /home/pi/
    - Copied the file "wareceive.py" to /home/pi/
    - Created a file named "testing.py" and copied the long code above into.
    - In row 6 I changed the phonenumber to mine and the password also
    - In row 37 I changed the second number also to my phonenumber
    - set the file executable with "sudo chmod 755 testing.py" and finaly try to run the file with
    - ./testing.py
    Then I got a few errors: ./testing.py: Zeile 1: import: Kommando nicht gefunden
    from: can't read /var/mail/wasend
    from: can't read /var/mail/wareceive
    ./testing.py: Zeile 5: Syntaxfehler beim unerwarteten Wort '('
    ./testing.py: Zeile 5: 'def credential():'

    What am I doing wrong? Any help appreciated, many thanks for your patience!

    ReplyDelete
    Replies
    1. The parser script can be downloaded from: http://www.mascal.it/public/parser.py
      Remember to change phone numbers and password before launching it of course.

      Delete
    2. I'am getting nuts...
      although I created a new file called testing.py and copied your linked contend from your "parser.py"-file into my testing.py, and although I made it executable with "sudo chmod 755 testing.py" and although I changed the credentials and although the file ist visible in /home/pi/ I get after "./testing.py" an error: "Datei oder Verzeichnis nicht gefunden" (compared in english like "file or path not found").
      Even after sudo reboot the same issue.....
      What else might I have done wrong? Obviously is the trouble not the raspbery pi but me in front of it....

      Delete
    3. Do not copy the content, rename the downloaded file and try with it.

      Delete
    4. Carlo, sorry for my stupid questions - how can I download the file "http://www.mascal.it/public/parser.py" without a link....?

      Delete
    5. That's a link! Copy and paste it in a browser or better just use the wget command from your rpi (wget http://www.mascal.it/public/parser.py)...

      Delete
    6. Carlo,
      I give up!
      Although downloaded this file via wget on my raspberry, changed the crendials and made the file again executable I got again the error: "File ./testing.py", line 14 return syntax error.
      What ever - this stuff is unfortuntely to high for my mind, I will try my luck with telegram. Again, many thanks for your patience with me, I will follow your very interesting blog!

      Delete
    7. Sorry to hear this... Unfortunately solving these kind of errors remotely can be quite difficult

      Delete
  20. It could be the same issue of testing.sh. As soon as possible I will try to provide the file for this script for you to download...

    ReplyDelete
  21. Hey Carlo after setting up my script didn't reply to me also after debug i found it goes to answer function and returns `need more than 1 value to unpack` can you please help ?

    ReplyDelete
    Replies
    1. Traceback (most recent call last):
      File "/home/pi/software/yowsup/pitalk.py", line 53, in
      else: Answer("Eh? What was that?")
      File "/home/pi/software/yowsup/pitalk.py", line 12, in Answer
      stack.start()
      File "/home/pi/software/yowsup/wasend.py", line 60, in start
      self.stack.loop()
      File "/home/pi/software/yowsup/yowsup/stacks/yowstack.py", line 175, in loop
      asyncore.loop(*args, **kwargs)
      File "/usr/lib/python2.7/asyncore.py", line 216, in loop
      poll_fun(timeout, map)
      File "/usr/lib/python2.7/asyncore.py", line 156, in poll
      read(obj)
      File "/usr/lib/python2.7/asyncore.py", line 87, in read
      obj.handle_error()
      File "/usr/lib/python2.7/asyncore.py", line 83, in read
      obj.handle_read_event()
      File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
      self.handle_read()
      File "/home/pi/software/yowsup/yowsup/layers/network/layer.py", line 72, in ha ndle_read
      self.receive(data)
      File "/home/pi/software/yowsup/yowsup/layers/network/layer.py", line 79, in re ceive
      self.toUpper(data)
      File "/home/pi/software/yowsup/yowsup/layers/__init__.py", line 55, in toUpper
      self.__upper.receive(data)
      File "/home/pi/software/yowsup/yowsup/layers/stanzaregulator/layer.py", line 2 8, in receive
      self.processReceived()
      File "/home/pi/software/yowsup/yowsup/layers/stanzaregulator/layer.py", line 4 8, in processReceived
      self.toUpper(oneMessageData)
      File "/home/pi/software/yowsup/yowsup/layers/__init__.py", line 55, in toUpper
      self.__upper.receive(data)
      File "/home/pi/software/yowsup/yowsup/layers/auth/layer_crypt.py", line 63, in receive
      self.toUpper(payload)
      File "/home/pi/software/yowsup/yowsup/layers/__init__.py", line 55, in toUpper
      self.__upper.receive(data)
      File "/home/pi/software/yowsup/yowsup/layers/coder/layer.py", line 35, in rece ive
      self.toUpper(node)
      File "/home/pi/software/yowsup/yowsup/layers/__init__.py", line 55, in toUpper
      self.__upper.receive(data)
      File "/home/pi/software/yowsup/yowsup/layers/__init__.py", line 160, in receiv e
      s.receive(data)
      File "/home/pi/software/yowsup/yowsup/layers/__init__.py", line 101, in receiv e
      recv(node)
      File "/home/pi/software/yowsup/yowsup/layers/auth/layer_authentication.py", li ne 61, in handleSuccess
      self.toUpper(nodeEntity)
      File "/home/pi/software/yowsup/yowsup/layers/__init__.py", line 55, in toUpper
      self.__upper.receive(data)
      File "/home/pi/software/yowsup/yowsup/layers/interface/interface.py", line 71, in receive
      self.callbacks[entityType](entity)
      File "/home/pi/software/yowsup/wasend.py", line 26, in onSuccess
      phone, message = self.getProp(self.__class__.PROP_MESSAGES, [])
      ValueError: need more than 1 value to unpack

      Delete
    2. I figured it out and fixed it, The problem with script is after first commands it takes time to reply for another command.

      Delete
    3. This comment has been removed by the author.

      Delete
  22. Hello I am getting error at Return as invalid syntax and unexpected indent if i remove return. Please help, i am using latest version files that you provided above.

    ReplyDelete
  23. def Answer(risp):
    try:
    stack=YowsupSendStack(credential(), ["390000000000", risp])
    stack.start()
    return


    Everything is running great. The only issue is in the above stack. For return it shows invalid syntax. And if we remove return it shows unexpected indent. Please help me out with this.

    ReplyDelete
    Replies
    1. Sorry for the delay, but I was on holiday...
      Python is quite sensible to code formatting ad the error you are getting seems related to this. Did you modify the code using windows notepad? If you did that, you should probably download again and use something different... If I need to change a text in a windows machine I always use Notepad++ as it's free and it doesn't change text coding.
      The other solution is to change the text directly into the RPi using nano or another Debian text editor (maybe with ssh connection from another computer).
      For the lines that give you the error, remember that the first line should be to the left border (no spaces before the "def..."). The second line and the last one should have 4 spaces before the text (spaces, NOT TABS) and the two "stack..." lines should be 8 spaces from the left border.

      Delete
    2. I also get this error message, but after I changed the syntax and gain except at the end, sometimes the message is not answered

      Delete
    3. This is a problem I found several times using Yowsup and that's one of the reasons why I'm not using it anymore. Unfortunately Yowsup completely lacks documentation, so it could be quite difficult to find if a problem is due to it or your implementation or whatever...
      If you do not need to act in real-time, you can use emails (I will write a post about this as I'm using this way to communicate with my Sphaera).

      Delete
    4. i have same problem, have another solution ??

      Delete
  24. Hi!
    First of all, thanks for providing this tutorial! They way you explain everything is really helpful!

    Unfortunately I running into the same problem as the users Karan makharia and dika. When I try to run the script it always complains about "return" (Line 14) as being invalid syntax. When I "solved" this by aligning it with the code from Line 13, it complains about def UpdateFunc(): in Line 16. Specifically it says "IndentationError: unexpected unindent".
    First I just used the file with the script you provided, then I copied it by typing it into a new file, but the same error is always the same. I also tried to change the spaces to 4 and 8 instead of using tabs. I believe I did this right with the spaces but the error is still the same.

    I am getting a little bit frustrated as this seems to be a "minor" problem, I expected to have other problems when the script actually runs and not this problem even before the script can actually run.

    So I was hoping you can tell from my description what I am doing wrong.

    Thanks in advance for your help!

    ReplyDelete
    Replies
    1. Python is quite sensitive to indentation. In my script I used just spaces (4 for every level). Anyway it should not give a syntax error, but just an indentation error. Maybe you can try to add an empty exception management like this:

      def Answer(risp):
      ____try:
      ________stack=YowsupSendStack(credential(), ["390000000000", risp])
      ________stack.start()
      ____except:
      ________pass
      ____return

      Of course change the underlines with spaces. Here I put them just to show the correct indentation.
      This should solve the problem. Tell me if this works, so that in case I will change the post and the downloadable script...

      Delete
    2. after i try, i have error:

      No handlers could be found for logger "yowsup.stacks.yowstack"
      Traceback (most recent call last):
      File "parser.py", line 38, in
      stack.start()
      File "/home/pi/yowsup/demos/wareceive.py", line 67, in start
      self.stack.loop(count=4)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/stacks/yowstack.py", line 195, in loop
      asyncore.loop(*args, **kwargs)
      File "/usr/lib/python2.7/asyncore.py", line 220, in loop
      poll_fun(timeout, map)
      File "/usr/lib/python2.7/asyncore.py", line 156, in poll
      read(obj)
      File "/usr/lib/python2.7/asyncore.py", line 87, in read
      obj.handle_error()
      File "/usr/lib/python2.7/asyncore.py", line 83, in read
      obj.handle_read_event()
      File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
      self.handle_read()
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/network/layer.py", line 86, in handle_read
      self.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/network/layer.py", line 94, in receive
      self.toUpper(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/stanzaregulator/layer.py", line 28, in receive
      self.processReceived()
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/stanzaregulator/layer.py", line 48, in processReceived
      self.toUpper(oneMessageData)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/auth/layer_crypt.py", line 63, in receive
      self.toUpper(payload)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/coder/layer.py", line 35, in receive
      self.toUpper(node)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/__init__.py", line 169, in receive
      s.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/__init__.py", line 105, in receive
      recv(node)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/protocol_messages/layer.py", line 20, in recvMessageStanza
      entity = TextMessageProtocolEntity.fromProtocolTreeNode(node)
      File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/protocol_messages/protocolentities/message_text.py", line 38, in fromProtocolTreeNode
      entity.setBody(node.getChild("body").getData())
      AttributeError: 'NoneType' object has no attribute 'getData'

      Delete
    3. This seems a yowsup problem. Maybe the installation is corrupt or you deleted something from the parser...

      Delete
  25. How do i use this script? When I open parser.py I get the error 'No handlers could be found for logger "yowsup.stacks.yowstack"
    Authentication Error: not-authorized'.
    What am I doing wrong?

    ReplyDelete
    Replies
    1. The warning about logger can be ignored without any problem.
      The other error just means that you did not register with whatsapp or that you made some mistake when copying the result of the registration process... You can take a look at the previous posts about yowsup to see how to proceed.

      Delete
    2. Ok, I set the credencials, now I get this:
      No handlers could be found for logger "yowsup.stacks.yowstack"
      Traceback (most recent call last):
      File "parser.py", line 37, in
      stack.start()
      File "/usr/local/bin/wareceive.py", line 67, in start
      self.stack.loop(count=4)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/stacks/yowstack.py", line 195, in loop
      asyncore.loop(*args, **kwargs)
      File "/usr/lib/python2.7/asyncore.py", line 220, in loop
      poll_fun(timeout, map)
      File "/usr/lib/python2.7/asyncore.py", line 156, in poll
      read(obj)
      File "/usr/lib/python2.7/asyncore.py", line 87, in read
      obj.handle_error()
      File "/usr/lib/python2.7/asyncore.py", line 83, in read
      obj.handle_read_event()
      File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
      self.handle_read()
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/network/layer.py", line 86, in handle_read
      self.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/network/layer.py", line 94, in receive
      self.toUpper(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/stanzaregulator/layer.py", line 28, in receive
      self.processReceived()
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/stanzaregulator/layer.py", line 51, in processReceived
      self.processReceived()
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/stanzaregulator/layer.py", line 51, in processReceived
      self.processReceived()
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/stanzaregulator/layer.py", line 48, in processReceived
      self.toUpper(oneMessageData)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/auth/layer_crypt.py", line 63, in receive
      self.toUpper(payload)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/coder/layer.py", line 35, in receive
      self.toUpper(node)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/__init__.py", line 59, in toUpper
      self.__upper.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/__init__.py", line 169, in receive
      s.receive(data)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/__init__.py", line 105, in receive
      recv(node)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/protocol_messages/layer.py", line 20, in recvMessageStanza
      entity = TextMessageProtocolEntity.fromProtocolTreeNode(node)
      File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/protocol_messages/protocolentities/message_text.py", line 38, in fromProtocolTreeNode
      entity.setBody(node.getChild("body").getData())
      AttributeError: 'NoneType' object has no attribute 'getData'

      Delete
    3. As I answered some comment above, this seems to be a yowsup issue. There could be some problem in the code or maybe whatsapp team changed something and in this case you need to install yowsup again to get the last updates...

      Delete
    4. to solve the problem add "YowAxolotlLayer" to wareceive.py

      like...
      (YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer),
      YowAxolotlLayer,
      ...

      Delete
    5. Now it works, but when I send a message to the automation it creates a log whatsapp.log with my "Wrong sender: [myphonenumber]". How I set the number as trusted?

      Delete
    6. Did you changed the following lines with your phone number (smartphone, not rpi) in all the three places? Also remember that the number should start with your country code (in the example 39 is for Italy).

      if received[:len("390000000000")]=="390000000000":
      received=received[len("390000000000"):]

      Delete
    7. I changed now, it didn't write in the log anymore, but I didn't received an answer when I sent 'hello' .

      Delete
    8. You can add the following line just before the if received[:5].... :
      print received
      withe the same indentation. This way you can check the incoming message to see if there is something wrong. The if statement for the hello message checks the first characters, so if you put a space or something else before the "hello" when sending to the RPi, this message cannot be recognized.
      Another problem could be the Answer routine. Did you changed the number there? It should be your smartphone number...

      Delete
    9. Hello Carlo,
      I tried your code today but won't work.
      I have the same problem as Geovani and tried your "print received".
      When I send the message "Hello" the received message is than "49xxxxxxxxhello□□□□□□" (x is my phone number) and no answer appears, even not the " else: Answer("Sorry, I cannot understand what you are asking for...")" message appears.
      I tried even to send a message by writing " Answer("Hallo") " directly beneath the if received[:len("49.... but tha won't work either.

      Delete
  26. Thanks for sharing beneficial information for the betterment of our knowledge about home automation and please keep sharing.

    Home Automation Vancouver | vancouver security | Best Security Vancouver

    ReplyDelete
  27. Hi,
    Do you notice with That kind of code using stack=YowsupSendStack and stack.start, sometimes it disconnect from Whatsapp, sometimes it keeps the connection 5 minutes, 20 minutes after that it does not response, And it is necessary to re-run the code (in order to receive all the messages or the answer that are on the stack or server). but it is problem of the whatsapp server, that sometimes avoid pings to the number, so the code keep blocked.

    I use this stack on a thread, but it keeps blocked, I am trying to use some kind of options like /disconnect that it is implemented with yowsup-cli. using event_disconnect, in order to re-connect in eache iteration.
    have you use this features of yowsup?have you explore a way to connect and disconnect in order to avoid the ping, or keeping the connection?

    ReplyDelete
    Replies
    1. Actually the loop should iterates 4 times and than it returns back to the main python script (as you can see at the end of the wareceive.py script), so it should disconnect after the end of the loops and should reconnect when called again. I did this way so I could do other things inside the parser, while waiting for an incoming messages..

      Delete
    2. OK. thanks a lot. It is a good example.

      Delete
  28. Hi,

    what do you think of this issue, It seems to be a Yowsup Problem, Sometimes it disconnect or it really difficult to get back the connection to the whatsapp server...

    File "SM_AdminWhatsapp.py", line 99, in ReadWhats
    stack.start()
    File "/home/pi/concentrador/SrvMm/Whatsapp/wareceive.py", line 87, in start
    self.stack.loop()
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/stacks/yowstack.py", line 195, in loop
    asyncore.loop(*args, **kwargs)
    File "/usr/lib/python2.7/asyncore.py", line 216, in loop
    poll_fun(timeout, map)
    File "/usr/lib/python2.7/asyncore.py", line 156, in poll
    read(obj)
    File "/usr/lib/python2.7/asyncore.py", line 87, in read
    obj.handle_error()
    File "/usr/lib/python2.7/asyncore.py", line 83, in read
    obj.handle_read_event()
    File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
    self.handle_read()
    File "/usr/local/lib/python2.7/dist-packages/yowsup2-2.4-py2.7.egg/yowsup/layers/network/layer.py", line 85, in handle_read
    data = self.recv(readSize)
    File "/usr/lib/python2.7/asyncore.py", line 387, in recv
    data = self.socket.recv(buffer_size)

    error: [Errno 11] Resource temporarily unavailable

    What are your appreciations?, re-install maybe, mistakes using the module?

    thanks.

    ReplyDelete
  29. It does not work every times but ... it works . Thank you.

    ReplyDelete
  30. hi,

    I modified the parser, ir order to connect and reconnect in every iteration. It means, that I used the event_Disconnect to stop the parser meanwhile I process other things.

    try:
    .....
    stack.start()

    except received..... :
    do something
    finally:
    stack.stop()

    I realized that the parser connects with stack.start() for 1 minute, waiting a Request, after that time, it pass to finally and disconnect the number, with the next iteration it runs again in order to process the request on the whatsapp server.
    If there is not a request, it is waiting for 1 minute, after that disconnect and connect again... (a good process maybe not lose the ping).

    Is possible to reduce or change that time (1 minute) ?, the time that the stack is still waiting (around 1 minute)


    Thanks

    ReplyDelete
    Replies
    1. Well, you could try to reduce it by changing the wareceive.py file.
      Almost at the end you can find this line:

      self.stack.loop(count=4)

      Just reduce the count value (keep it grater than 0) and try.

      Delete
  31. Carlo, which demo on echoclient do i have changing ?

    ReplyDelete
  32. Hi Carlo, i need to know which folder i have to put the parser.py file and how i execute it .. thanks

    I hope that you can help me

    Best Regards

    ReplyDelete
    Replies
    1. You can put the script where you prefer. It can be executed from every folder without problems.
      Of course the other two scripts (wasend and wareceive) must be in the same folder with the parser.
      As it's a normal python script, just launch it like every other python script:
      python parser.py

      Delete
  33. Hi Carlo,

    Do you know how Yowsup is stable?, I had the parser.py runs all the time, and suddenly stop sending messages from the interruptions on the Rpi pins. the output was "authorized error: UnAuthorized number".

    when I try to ask for a new code with two SIM numbers, the result was:
    INFO:yowsup.common.http.warequest:{"status":"fail, "reason": "old_version"},

    maybe is there a new update?, I assume that yowsup is not stable to work with it.

    what ddo you thing about it?



    ReplyDelete
    Replies
    1. Unfortunately Whatsapp makes changes from time to time. Yowsup creator is quite fast to reproduce the updates in his code, but you need to install it again.
      To use it without too much trouble, you have to check quite often if there is an update and act consequently.
      This is one of the reasons why I do not use it anymore.

      Delete
    2. I got it, yeah it is unstable, I fixed erasing the .yowsup folder

      for example:
      Another thing is, when I use the yowsup-cli demos -c config.conf --yowsup and I am logged, I have all the interactions with people, I add people with /contacts sync #'s, I received and send text messages.

      BUT When I run the Answer code, I don't receive the text alarm or any message, only images. Why is that? or sometimes I send to 4 numbers but the message is only received by one or two numbers.

      any ideas to fixed.

      thanks.

      Delete
    3. My scripts have been created just for simple notifications between RPi and one smartphone and they have only a small subset of all the functions implemented within Yowsup.
      If you need more functionality you can go through the yowsup-cli code and see how to create a new script or how to modify mine.

      Delete
  34. When wareceive.py raise the message it doesnt enter on line: "except MessageReceived as rcvd:".
    I put a print below but nothing.
    On wareceive.py I put a print on class "MessageReceived(Exception):" and works.
    Do you have some tip?

    ReplyDelete
    Replies
    1. If the exception is raised and it doesn't enter the except... statement, then the script will stop. If it doesn't stop, the except is managed as the except statement is executed.
      The problems could be a wrong indentation of the print or maybe you are not printing the value of the exception...

      Delete
  35. Hello Carlo,
    I updated yowsup some days ago, since than following error appears:
    File "/home/pi/pywork/pitalk.py", line 35, in
    stack=YowsupReceiveStack(credential())
    File "/home/pi/pywork/wareceive.py", line 63, in __init__
    self.stack.setProp(YowCoderLayer.PROP_RESOURCE, env.CURRENT_ENV.getResource())
    AttributeError: 'module' object has no attribute 'CURRENT_ENV'

    is it possible to update your files wasend.py & wareceive.py, too?
    thank you for your work.
    Fin

    ReplyDelete
    Replies
    1. Sorry, but I don't think I will update. This year I'm quite busy with several different things (as you may have seen I'm not posting new articles quite often).
      Also I'm really not using yowsup anymore and what happened to you clearly explain why: Whatsapp staff makes changes, so Yowsup staff makes changes and you have to make changes. Most of your efforts are on keeping the things updated or they won't work.
      Yowsup is ok for hobbists with spare time, but not for the final users.

      There are other easier ways to communicate with a RPi and that do not need constant maintenance.

      Delete
    2. what are the easier ways? i tried mail, and i don't like it, sms needs an umts stick. any advice?

      Delete
    3. Most depends on what are your needs...

      You do not like emails, so we can leave these apart, even if they can also be used to voice-control your RPi (not exactly direct control, but today almost every smartphone allows to send an email by just telling them to do it and they also can read them aloud when received).

      If you really need to "chat" with a RPi you can use different softwares. Telegram is a nice substitute, but I have not tried it.

      MQTT is another nice way to try and I will definitely check this in the future.

      For just receiving notifications on the smartphone you can use something like Pushover (not free, but the price is low) that is quite easy to use. This is my preferred way.

      If you do not need almost real-time communication, a web interface will do the job. It's free and after you create it there is really few maintenance needed.

      These are just examples, but cover many needs. Of course you could just wish to stay with Yowsup anyway. In this case I will suggest you to deeply study the yowsup-cli code and look at the developer site to see all the requests by other users (and you can also ask, of course).
      It's not too easy and maybe it could take some time to really manage that software, but it's not an impossible task.
      Please note that before starting with RPi I knew nothing about Python (even if I am an expert programmer with other languages) and in few months I managed to use Yowsup, so it's just a matter of study, trials and errors...

      Delete
  36. No handlers could be found for logger "yowsup.stacks.yowstack" ?????

    ReplyDelete
    Replies
    1. With Yowsup you can also create a log, to see what's happening. It's just a debugging feature.
      You can ignore this message, but if you wish to use this feature you can search on internet for "python logging" for more info as Yowsup should use the standard python logging system.

      Delete
  37. Hi carlo, I don't have raspi, but I want test it myself. is it possible to use the script on pc or laptop that running ubuntu?

    ReplyDelete
    Replies
    1. The answer is yes...and no.
      If you just wish to use yowsup, there should be no problem at all. You could have issues if you wish to use something specific to the RPi, like GPIOs for example.

      Delete
  38. hi, anyone can help me? i got my whatsapp to send message from raspberry but my whatsapp in phone is logout, i cant received message from rpi,pls help

    ReplyDelete
    Replies
    1. Are you usIng the same phone number for yowsup and your phone? If this is the case stop immediately as you risk your number to be banned forever from whatsapp!

      Delete
    2. what i must to do? can u help me pls, i need it for my little project

      Delete
    3. You need to use a different number for the Raspberry, there are nothing else you can do. Unfortunately Whatsapp keeps track of the connections used by every number and the use on multiple devices at the same time is forbidden.

      If you cannot borrow a number from someone (I used the one from my ipad as on this device there isn't Whatsapp and of course you also need to access this number as you need to receive the sms for the password) the only solution is to use a different way to communicate with your RPi.

      Also for this problem, I do not use Yowsup anymore. Now I'm using Telegram as you do not need a phone number for the RPi and the developer allows you to use this software on every device you wish. You can find some more info on newer posts.

      Delete