Read a text file

There are a couple of threads about this but all are very old and inconclusive so instead of reviving something I’m asking here.

I had the idea to read a text file and select a random line from it. This seems to me to be something that would be useful in all those TTS messages that I and others are using with some randomising of (for example) greetings and conjunction phrases between items of information.

This would have the added advantage of being updateable without having to restart HA.

I know nothing about python but some reading led me to this, https://docs.python.org/2/library/linecache.html which explains

linecache.getline(filename, lineno[, module_globals])

I wondered if shell_command might be the way to go but it seems like two lines are needed so as to include the library.

I did try writing my own component/service (following the docs) to do this using a file of one line jokes as a test but whilst HA doesn’t complain and the service can be called by the development interface, it doesn’t seem to return anything (at the moment it is just trying to return line 1 of the file).

DOMAIN = 'myjoke_service'

def setup(hass,config):

    def handle_myjoke(call):
    
        import linecache
        joke = linecache.getline('jokes.txt', 1)
    
        hass.states.set('myjoke_service.joke', joke)

    hass.services.register(DOMAIN, 'joke', handle_myjoke)

    return True

Like I said, python is not my thing so if I am too far out of my depth just tell me!

Looks like you are setting your state to an object that shares the same name as your service. Not sure if that’s good. Anways, after you run the service, you should check your state objects to see if that joke updated. The only thing I notice is that you are always reading line 1 so the joke will never change.

Yes, I think on reflection it isn’t such a great idea anyway.
Firstly you need to know how many lines there are in the file before you can pass it a number and also the path to the files always has to be considered.

I think I’ll stick to !includes. Wiser people than me use them - there must be a reason!

It was an interesting and informative diversion though and some first steps into the inner workings of HA.

FWIW, you could do something like this:

import random

DOMAIN = 'myjoke_service'

def setup(hass,config):

    def handle_myjoke(call):
        with open(hass.config.path('jokes.txt')) as f:
            joke = random.choice(list(f)).strip()
    
        hass.states.set('myjoke_service.joke', joke)

    hass.services.register(DOMAIN, 'joke', handle_myjoke)

    return True

First, this assumes the file is “relatively” short. I suspect this would be the case.

Second, regarding the file name, you have to be careful about that. I believe that the “working directory” is “/”, which means unless the file is in the file system’s root directory, that probably won’t work. Generally you’ll want the file in your HA config folder. That’s why the code above uses the hass.config.path() function.

Regarding the service and entity having the exact same names, I’m not sure, but I believe they are effectively in different “name spaces”, so it will probably be ok.

Disclaimer: I haven’t tried this (other than the algorithm for picking a random line, which I just got by Googling :slight_smile:). Also you may not want this component to be this simple, but at least this might be a start???

1 Like