joshpirihi / meshtastic-mqtt

A python script to translate Meshtastic packets into a plain format. Currently sends positions to a Traccar instance, and publishes battery % and environmental plugin data to its own topic (eg for Grafana)
32 stars 9 forks source link

Detailed instructions for Home Assistant #14

Open Nibb31 opened 2 years ago

Nibb31 commented 2 years ago

Hi, Could you give some more precise instructions on how to use this with Home Assistant (and AppDaemon). Do all the scripts need to be in AppDaemon or only the main meshtastic-mqtt.py? What to put in apps.yaml? What exactly does "on appdaemon remove the argument here" mean ? How do you get a device/entities that you can interact with in Home Assistant ?

joshpirihi commented 2 years ago

All Ive been using app daemon for is a place to leave this script running. It doesn't actually interact with home assistant at all. I'd love to make a proper integration for home assistant sometime, that would probably use this script as a major component

Here's what my apps folder looks like. This repo has diverged from the script I've got in there at the moment, I might have a look to see if there's a way to run a script from pip in home assistant somehow.

Screen Shot 2022-02-19 at 6 51 04 AM
gfzmake commented 2 years ago

Hi, I put this files in appdaemon but how can runit ?

joshpirihi commented 2 years ago

Edit the meshtastic_mqtt.py script, and import the hassapi stuff and change the class definition:

#uncomment for AppDaemon
import hassapi as hass

#swap for AppDaemon
class MeshtasticMQTT(hass.Hass=None):
#class MeshtasticMQTT():

Then down the bottom comment out this bit:

#in appdaemon comment this block out
#if __name__ == '__main__':
#    main()

Finally, restart AppDaemon and watch the Log tab as it starts up, you should eventually see a message saying meshtastic-mqtt has started:

2022-02-18 19:52:38.561113 INFO AppDaemon: Initializing app meshtastic-mqtt using class MeshtasticMQTT from module meshtastic-mqtt

gfzmake commented 2 years ago

image Looks promising I have to do something else ?

joshpirihi commented 2 years ago

EDIT THIS IS WRONG... SE BELOW

Oops yea you need to add this to apps.yaml:

meshtastic-mqtt:
  module: "meshtastic-mqtt"
  class: MeshtasticMQTT
gfzmake commented 2 years ago

image

joshpirihi commented 2 years ago

I got that wrong. Sorry the version mismatch has me behind a little. Your apps.yaml should be

meshtastic_mqtt:
  module: "meshtastic_mqtt"
  class: MeshtasticMQTT
gfzmake commented 2 years ago

image image image

joshpirihi commented 2 years ago

Getting there! Remove the =None so the class definition looks like class MeshtasticMQTT(hass.Hass):

gfzmake commented 2 years ago

Aright... image image image

joshpirihi commented 2 years ago

Ok I really need to do some work here I guess. Below is my entire script that is running on my AppDaemon. It's a bit different to what is in this repo but it is working on my one:

# python3.6

from portnums_pb2 import ENVIRONMENTAL_MEASUREMENT_APP, POSITION_APP
import random
import json

import mesh_pb2 as mesh_pb2
import mqtt_pb2 as mqtt_pb2
import environmental_measurement_pb2

from paho.mqtt import client as mqtt_client

import requests

import hassapi as hass

class MeshtasticMQTT(hass.Hass):

    broker = '10.147.253.250'
    port = 1883
    topic = "msh/1/c/#"
    # generate client ID with pub prefix randomly
    client_id = f'python-mqtt-{random.randint(0, 100)}'
    # username = 'emqx'
    # password = 'public'

    traccarHost = '10.147.253.250'

    def connect_mqtt(self) -> mqtt_client:
        def on_connect(client, userdata, flags, rc):
            if rc == 0:
                print("Connected to MQTT Broker!")
            else:
                print("Failed to connect, return code %d\n", rc)

        client = mqtt_client.Client(self.client_id)
        client.username_pw_set("user", "pass")
        client.on_connect = on_connect
        client.connect(self.broker, self.port)
        return client

    def subscribe(self, client: mqtt_client):
        def on_message(client, userdata, msg):
            #print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
            se = mqtt_pb2.ServiceEnvelope()
            se.ParseFromString(msg.payload)

            print(se)
            mp = se.packet
            if mp.decoded.portnum == POSITION_APP:
                pos = mesh_pb2.Position()
                pos.ParseFromString(mp.decoded.payload)
                print(getattr(mp, "from"))
                print(pos)
                owntracks_payload = {
                    "_type": "location",
                    "lat": pos.latitude_i * 1e-7,
                    "lon": pos.longitude_i * 1e-7,
                    "tst": pos.time,
                    "batt": pos.battery_level,
                    "alt": pos.altitude
                }
                if owntracks_payload["lat"] != 0 and owntracks_payload["lon"] != 0:
                    #client.publish("owntracks/"+str(getattr(mp, "from"))+"/meshtastic_node", json.dumps(owntracks_payload))
                    if len(self.traccarHost) > 0:
                        traccarURL = "http://"+self.traccarHost+":5055?id="+str(getattr(mp, "from"))+"&lat="+str(pos.latitude_i * 1e-7)+"&lon="+str(pos.longitude_i * 1e-7)+"&altitude="+str(pos.altitude)+"&battery_level="+str(pos.battery_level)+"&hdop="+str(pos.PDOP)+"&accuracy="+str(pos.PDOP*0.03)
                        print(traccarURL)
                        submitted = requests.get(traccarURL)
                        print(submitted)
                #lets also publish the battery directly
                if pos.battery_level > 0:
                    client.publish("/mesh/"+str(getattr(mp, "from"))+"/battery", pos.battery_level)
            elif mp.decoded.portnum == ENVIRONMENTAL_MEASUREMENT_APP:
                env = environmental_measurement_pb2.EnvironmentalMeasurement()
                env.ParseFromString(mp.decoded.payload)
                print(env)
                client.publish("/mesh/"+str(getattr(mp, "from"))+"/temperature", env.temperature)
                client.publish("/mesh/"+str(getattr(mp, "from"))+"/relative_humidity", env.relative_humidity)

        client.subscribe(self.topic)
        client.on_message = on_message

    def run(self):
        client = self.connect_mqtt()
        self.subscribe(client)
        client.loop_forever()

    def initialize(self):
        self.run()

#if __name__ == '__main__':
#    mm = MeshtasticMQTT()
#    mm.run()
gfzmake commented 2 years ago

I had to uncomment mqtt password image image image

joshpirihi commented 2 years ago

Oh yea the protobufs have changed too.. I will create a seperate repo for this and push up my current working files. Its diverged too much. Stand by...

joshpirihi commented 2 years ago

https://github.com/joshpirihi/meshtastic-mqtt-appdaemon

gfzmake commented 2 years ago

Great work!

gfzmake commented 2 years ago

image image Still need some tweaks

joshpirihi commented 2 years ago

That's working! You can ignore all the warnings there, it's just ignoring the protobufs as it should. If you connect a node to your mqtt you should now get some data out of it

gfzmake commented 2 years ago

image Where should it appear ?

joshpirihi commented 2 years ago

it should create a meshtastic/ topic tree. What do you see on the AppDaemon Log tab now? Is the broker and username and password set properly?

I've just tweaked the main script on the repo to make the mqtt credentials a bit easier to set, they were hard coded.

gfzmake commented 2 years ago

image image image In config of username and password username said "usename" image

joshpirihi commented 2 years ago

Should be fixed now

gfzmake commented 2 years ago

image image

joshpirihi commented 2 years ago

Does your mqtt broker require credentials? If it doesn't, you'll need to comment out the line client.username_pw_set()

gfzmake commented 2 years ago

Yes my mqtt require credentials.

gfzmake commented 2 years ago

image Yeah, same error... image I have these packages installed

joshpirihi commented 2 years ago

What does your whole script look like? Are you defining username and password? It looks like it may be an issue with them

gfzmake commented 2 years ago

This script? image image image

joshpirihi commented 2 years ago

Ohh sorry man my python is not great. Change the username_pw_set to

        client.username_pw_set(self.username, self.password)
gfzmake commented 2 years ago

Success! connected to the mqtt but still not decode the message. image

joshpirihi commented 2 years ago

As packets are published by your gateway node, meshtastic-mqtt will translate them. That Log page should start showing some traffic if the mqtt is connected up properly

gfzmake commented 2 years ago

image

gfzmake commented 2 years ago

This ?

joshpirihi commented 2 years ago

Yep that's it! That is showing that the packets are still encrypted. Most likely you'll need to upgrade the firmware on your gateway node to at least 1.2.54. Support for on-device decryption was added part way through 1.2.53

gfzmake commented 2 years ago

I have two nodes, one encrypted and the other free. image

joshpirihi commented 2 years ago

Which one is connected to mqtt? And what firmware version is it running? Setting a blank encryption key isn't enough for this particular task

gfzmake commented 2 years ago

Is 1.2.55

joshpirihi commented 2 years ago

What is the output of meshtastic --info for that node?

gfzmake commented 2 years ago

Now the node is at the top of the roof... I had configured it with this example. image

joshpirihi commented 2 years ago

Where is that documentation from? It's the wrong way around... that needs to be false. You can configure it with the cli via the network mestastic --host 192.168.1.2 --set mqtt_encryption_enabled false

gfzmake commented 2 years ago

Really great! I need to reboot the node after change settings ?

joshpirihi commented 2 years ago

I don't think so, but it won't hurt to reboot

gfzmake commented 2 years ago

i'm restarting Home Assistant

gfzmake commented 2 years ago

Looks like it's working! But only when I reboot or stop AppDaemon. image

In mqtt the topic "meshtastic" still does not appear.

joshpirihi commented 2 years ago

try subscribing to /mesh/# instead

joshpirihi commented 2 years ago

There isn't currently support for text messages. Ill add it now

gfzmake commented 2 years ago

Ok great!

joshpirihi commented 2 years ago

I've updated the script here. That should work a bit better

gfzmake commented 2 years ago

image image Stil decode messages does not appear in mqtt

joshpirihi commented 2 years ago

Just tweaked the script again, could you give it another go?

gfzmake commented 2 years ago

Sure