5500 SHAC / MQTT / Raspberry PI / Home Assistant / Tuya

Discussion in 'C-Bus Automation Controllers' started by impact, Nov 23, 2021.

  1. impact

    impact

    Joined:
    Feb 10, 2008
    Messages:
    116
    Likes Received:
    13
    I am confused a little - and its because everything evolves, something new is found, simplified, and the internet is fantastic for digging up all these old posts... So what is new is confused with what is old....

    I have previously set up an Android to do Home Bridge - got google voice working - it was fun - but I have out grown it - and now believe Home Assistant is more of what I need to integrate...

    Recently I have installed a couple of devices - which are controlled through Tuya - a pain - but specialised, and thats the way the manufacturer went... I want to turn these devices on and off from a clispal 2 gang wall switch on cbus... so need to integrate with something with tuya.

    Home Assistant has now got official integration with Tuya, and the promise of a future offline mode excites me... While there are libraries for Home Bridge - they dont seem to be that complete, and pllus I could use the Home Assistant Tiles / Webcast functionality for the better!


    So my understanding is I need CBUS to talk to Home Assistant via MQTT.

    Was looking at using a Rapsberry PI, several posts to set up CGate / MQTT etc - but most appear to rely upon hard wired RS232 interface or similar... When I used Home Assistant I just used my network and connected to the SHAC as a CNI

    Then I noticed more recent posts discussing using the SHAC as MQTT directly - so is this what I want to do...

    How do I get my SHAC to broadcast / connect to the Raspberry PI running Home Assistant
     
    impact, Nov 23, 2021
    #1
    1. Advertisements

  2. impact

    paddyb

    Joined:
    Aug 9, 2020
    Messages:
    4
    Likes Received:
    0
    Location:
    Ireland
    Hi,
    I have more experience with OpenHab than Homeassitant specifically, but the principles of what you need to do should be the same. There are a number of ways to do what you need, and you've kinda touched on a few of them already...

    You can use Cgate if you like, running on a RPI for example, and have cgate connect to the actual C-Bus via RS232 as you have seen, via a 5500PC interface, or you can also skip the serial link if you prefer, and do an IP connection to a 5500CN2 unit instead. Both of these methods would leave your SHAC connections free, and allow you to use for other things... Once C-gate is up and running, you can use this project on github to get the information over and back to a separate MQTT broker... and lots of guides for all of this on the addicted-to-pi blog/site. Either of these ways still work and are perfectly valid..

    Probably easier to use what you have already though...The SHAC device can only act as an MQTT 'client', so you will still need the server part of MQTT, sometimes called a broker, running somewhere else. Homesasistant may have one available as a plugin, or if it's running on an RPI or some kind of Linux server, it's trivial enough to install the Mosquitto MQTT broker yourself. Then, there are some excellent LUA scripts on this GitHub project - to interface the SHAQ to MQTT directly, skipping the C-gate method completely... set these scripts up on your SHAC and you will be hooked up to your MQTT server of choice pretty easily...

    One word of caution, it's tempting to install everything on one RPI, and it will work for a while, but then reboot it and see what happens! you can have a lot of issues with one thing starting before another etc - I have found it way more reliable to use a few separate RPIs or Odroid boards... it's way easier to fault-find... Your mileage may vary, and all that...

    Personally, my current setup is that I use a SHAC (with Lua scripts above) > RPI '#1' running Mosquitto Broker > Openhab3 on RPI '#2'.
    Works really well for me. I use RPI-4s booting and running from SSDs at the moment, a vast improvement over the SD cards..
     
    paddyb, Nov 23, 2021
    #2
    1. Advertisements

  3. impact

    pspeirs

    Joined:
    Nov 23, 2013
    Messages:
    171
    Likes Received:
    6
    Location:
    Sydney
    Have recently installed HA onto a Pi4 with SSD. I'm using the Argon One case to house it all, and it is worth the extra. Don't try using the Pi with SD, you'll be asking for trouble.

    You will install the HA OS onto the SSD after first installing RPi OS. There is a process for that and it is easy as. I wanted to document it for my own benefit, may still do that.

    From HA, you can install the Mosquitto MQTT broker as an addon.

    In the SHAC, you will run a couple of scripts, one to publish to the broker, and one to subscribe.

    cbus2mqtt (Resident script)

    Code:
    -- A resident script to run on a Clipsal 5500SHAC to push Cbus events to MQTT
    -- Tested with 5500SHAC firmware v1.6
    -- Install this script as a resident script with a sleep interval of 5 seconds
    --log("Starting CBUS2MQTT")
    
    local connection_state = 0
    
    mqtt_broker = '10.1.1.50'
    mqtt_username = 'xxxxxxx'
    mqtt_password = 'xxxxxxx'
    
    mqtt_read_topic = 'cbus/read/'
    
    -- load mqtt module
    mqtt = require("mosquitto")
    
    -- create new mqtt client
    client = mqtt.new("CBUS2MQTT")
    
    
    --log(string.format("CBUS2MQTT - Created MQTT client: %s", client))
    
    -- C-Bus events to MQTT local listener
    server = require('socket').udp()
    server:settimeout(1)
    server:setsockname('127.0.0.1', 5432)
    log("CBUS2MQTT - Opened UDP Socket on port 5432")
    
    
    client.ON_CONNECT = function(client, userdata, flags, rc)
    --  log("MQTT connected - send")
        log(string.format("CBUS2MQTT - MQTT Client Connected: Result Code: %s Publish: %s", mqtt_broker, flags, mqtt_read_topic))
        log(client, userdata, flags, rc)
        connection_state = 1
    end
    
    client.ON_DISCONNECT = function()
        connection_state = 2
        log("MQTT Client Disconnected")
    end
    
    
    client:login_set(mqtt_username, mqtt_password)
    client:connect(mqtt_broker)
    client:loop_start()
    
    
    -- Using this to update the retain flag for testing
    -- Reset all channels to not retaining if needed
    --for i = 77, 77 do
    --    client:publish(mqtt_read_topic .. "254/56/" .. i .. "/state", "", 1, true)
    --    client:publish(mqtt_read_topic .. "254/56/" .. i .. "/level", "", 1, true)
    --    log("Message Sent Grp " .. i)
    --end
    
    
    while true do
        cmd = server:receive()
        if cmd then
            --log(string.format('CBUS2MQTT - UDP Msg Recvd: %s', cmd))
    
            parts = string.split(cmd, "/")
            network = 254
            app = tonumber(parts[2])
            
            if (app == 228) then
                --log(string.format('Measurement Message: %s', cmd))
                device_id = tonumber(parts[3])
                channel_id = tonumber(parts[4])
                value = tonumber(parts[5])
    
                mqtt_msg = string.format('%s%u/%u/%u/%u', mqtt_read_topic, network, app, device_id, channel_id)
                --log(mqtt_msg)
                client:publish(mqtt_msg .. "/measurement", value, 1, false)
    
            elseif (app == 203) then    -- Enable Control
                group = tonumber(parts[3])
                level = tonumber(parts[4])
                state = (level ~= 0) and "ON" or "OFF"
                --log("CBUS2MQTT - Publish: " .. mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/state " .. state)
                --log("CBUS2MQTT - Publish: " .. mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/level " .. level)
    
                client:publish(mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/state", state, 1, true)
                client:publish(mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/level", level, 1, true)
    
            elseif (app == 250) then    -- User Parameters
                group = tonumber(parts[3])
                level = tonumber(parts[4])
    
                mqtt_msg = string.format('%s%u/%u/%u/level', mqtt_read_topic, network, app, group)
                --log(mqtt_msg, level)
                client:publish(mqtt_msg, level, 1, false)
                
            else
                group = tonumber(parts[3])
                level = tonumber(parts[4])
                state = (level ~= 0) and "ON" or "OFF"
                --log("CBUS2MQTT - Publish: " .. mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/state " .. state)
                --log("CBUS2MQTT - Publish: " .. mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/level " .. level)
    
                client:publish(mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/state", state, 1, true)
                client:publish(mqtt_read_topic .. network .. "/" .. app .. "/" .. group .. "/level", level, 1, true)
            end
        end
        
        --if (connection_state == 2) then
            --client.reconnect(mqtt_broker)
        --end
    end
    

    mqtt2cbus (resident script)

    Code:
    -- A resident script to run on a Clipsal 5500SHAC to push MQTT events to Cbus.
    
    -- Tested with 5500SHAC firmware v1.6
    
    -- Install this script as a resident script with a sleep interval of 5 seconds
    
    --log("Starting MQTT2CBUS")
    
    mqtt_broker = '10.1.1.50'
    mqtt_username = 'xxxxxxx'
    mqtt_password = 'xxxxxxx'
    
    mqtt_read_topic = 'cbus/read/'
    mqtt_write_topic = 'cbus/write/#';
    
    -- load mqtt module
    mqtt = require("mosquitto")
    
    -- create new mqtt client
    client = mqtt.new("MQTT2CBUS")
    
    --log(string.format("MQTT2CBUS - Created MQTT client: %s", client))
    
    
    
    client.ON_CONNECT = function()
        local mid = client:subscribe(mqtt_write_topic, 2)
        log(string.format("MQTT2CBUS - MQTT connected: %s Subscribe: %s", mqtt_broker, mqtt_write_topic))
    
    end
    
    
    
    client.ON_MESSAGE = function(mid, topic, payload)
    
        --log(string.format("MQTT2CBUS - Received: %s %s", topic, payload))
          parts = string.split(topic, "/")
    
      if not parts[6] then
        
        log('MQTT2CBUS - MQTT error', 'Invalid message format')
    
      elseif parts[6] == "getall" then
        
        datatable = grp.all()
        for key,value in pairs(datatable) do
          dataparts = string.split(value.address, "/")
              network = tonumber(dataparts[1])
              app = tonumber(dataparts[2])
          group = tonumber(dataparts[3])
          if app == tonumber(parts[4]) and group ~= 0 then
                level = tonumber(value.data)
                state = (level ~= 0) and "ON" or "OFF"
                log(parts[3], app, group, state, level)
                client:publish(mqtt_read_topic .. parts[3] .. "/" .. app .. "/" .. group .. "/state", state, 1, true)
                client:publish(mqtt_read_topic .. parts[3] .. "/" .. app .. "/" .. group .. "/level", level, 1, true)
              end   
          end
        log('Done')
      elseif parts[6] == "switch" then
            --log("Incoming Message")
        if payload == "ON" then
            --log(string.format("MQTT2CBUS - SetCBusLevel(0, %u, %u, 255, 0)", parts[4], parts[5]))
            SetCBusLevel(0, parts[4], parts[5], 255, 0)
        elseif payload == "OFF" then
            --log(string.format("MQTT2CBUS - SetCBusLevel(0, %u, %u, 0, 0)", parts[4], parts[5]))
              SetCBusLevel(0, parts[4], parts[5], 0, 0)
        end
        
      elseif parts[6] == "measurement" then
    
        SetCBusMeasurement(0, parts[4], parts[5], (payload / 10), 0)
        
      elseif parts[6] == "ramp" then
    
        if payload == "ON" then
                SetCBusLevel(0, parts[4], parts[5], 255)
        elseif payload == "OFF" then
              SetCBusLevel(0, parts[4], parts[5], 0)
        else
          ramp = string.split(payload, ",")
          num = math.floor(ramp[1] + 0.5)
          if num and num < 256 then
            if ramp[2] ~= nil and tonumber(ramp[2]) > 1 then
                SetCBusLevel(0, parts[4], parts[5], num, ramp[2])
            else
                SetCBusLevel(0, parts[4], parts[5], num, 0)
            end
          end
        end
        
      end
    end
    
    client:login_set(mqtt_username, mqtt_password)
    client:connect(mqtt_broker)
    client:loop_forever()
    
    You will also need an event script (call_cbus2mqtt) or whatever you want, that triggers on a tag (MQTT_Grp) for example.

    Code:
    -- This script efficiently pushes Cbus events to the cbus2mqtt resident
    -- script via an internal socket without having to reestablish an MQTT
    -- connection for each and every event.
    
    -- Install this script as an event based script against the keyword "MQTT_Grp"
    
    -- You'll need to tag every object with the "MQTT_Grp" keyword so this script is run
    -- whenever an object changes. You can do this easily by running a function
    -- like:
    
    --for i = 0, 10 do
    --   grp.addtags('0/56/'..i, 'MQTT_Grp')
    --end
    
    --log(string.format('CALL_CBUS2MQTT - UDP Msg Sent: %s/%s', event.dst, event.getvalue()))
    require('socket').udp():sendto(event.dst .. "/" .. event.getvalue(), '127.0.0.1', 5432)
    Now, add a tag to every group, measurement, user param, etc that you want to send across to the MQTT broker.
    The scripts were originally from someone else on this forum and have been modified to suit.

    There is still some work to do when I get time, like re-establishing the link to the MQTT broker, have been some instances when I've had HA offline and the script has failed to reconnect. I'll use the ON_DISCONNECT function when I get around to it.


    Cheers,
    Paul
     
    pspeirs, Nov 23, 2021
    #3
    KevinH likes this.
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.