Been battling all day with an issue with reading/writing UserParams from a Resident Script Tried writing a library and move all commands out to there tested worked fine Tried calling them from the resident script and they silent fail. its based on the MQTT script floating around but was extending to tie into HA closer The issue is in the hvac cmd The AC are connected by modbus and mapped to UserParms HW: 5500SHAC (i.MX28) SW: 1.14.0 Code: mqtt_broker = '192.168.2.15' mqtt_username = 'cbus' mqtt_password = 'xxxxxxx' mqtt_clientid = 'mqtt2cbus' mqtt_write_topic = 'cbus/write/#'; -- load mqtt module mqtt = require("mosquitto") -- create new mqtt client client = mqtt.new(mqtt_clientid) log("created MQTT client", client) client.ON_CONNECT = function() log("MQTT connected - receive") local mid = client:subscribe(mqtt_write_topic, 2) end client.ON_MESSAGE = function(mid, topic, payload) -- Topic: cbus/write/cat/net/app/grp/cmd log(topic, payload) parts = string.split(topic, "/") if not parts[5] then -- Invalid message format log('MQTT error', 'Invalid message format') elseif string.lower(parts[3]) == "light" then -- Topic: cbus/write/light/grp/switch|ramp net = 0; app = 56 grp = tonumber(parts[4]) cmd = string.lower(parts[5]) log('LIGHT Command: ' .. net .."/".. app .."/".. grp .. "/" .. cmd .. " :: " .. payload) if cmd == 'switch' then if string.upper(payload) == "ON" then SetCBusLevel(net, app, grp, 255, 0) elseif string.upper(payload) == "OFF" then SetCBusLevel(net, app, grp, 0, 0) end elseif cmd == "ramp" then if string.upper(payload) == "ON" then SetCBusLevel(net, app, grp, 255, 0) elseif string.upper(payload) == "OFF" then SetCBusLevel(net, app, grp, 0, 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(net, app, grp, num, ramp[2]) else SetCBusLevel(net, app, grp, num, 0) end end end end elseif string.lower(parts[3]) == "fan" then -- Topic: cbus/write/fan/grp/switch|speed net = 0; app = 56 grp = tonumber(parts[4]) cmd = string.lower(parts[5]) log('FAN Command: ' .. net .."/".. app .."/".. grp .. "/" .. cmd .. " :: " .. payload) if cmd == 'switch' then if string.upper(payload) == "ON" then SetCBusLevel(net, app, grp, 255, 0) elseif string.upper(payload) == "OFF" then SetCBusLevel(net, app, grp, 0, 0) end elseif cmd == "speed" then speed = tonumber(payload) -- See if speed or a numeric value if speed ~= nil then if speed == 0 then SetCBusLevel(net, app, grp, 0, 0) elseif speed == 1 then SetCBusLevel(net, app, grp, 84, 0) elseif speed == 2 then SetCBusLevel(net, app, grp, 170, 0) elseif speed == 3 then SetCBusLevel(net, app, grp, 255, 0) end end end elseif string.lower(parts[3]) == "hvac" then -- Topic: cbus/write/hvac/grp/switch|fan|mode|vane|temp net = 0; app = 250 grp = tonumber(parts[4]) cmd = string.lower(parts[5]) log('HVAC Command: ' .. net .."/".. app .."/".. grp .. "/" .. cmd .. " :: " .. payload) if cmd == 'switch' then --aircon_set_power(grp, payload) --SetUserParam('Local, 'Test_AC_Switch', false) --SetUserParam('Local Network', 'Test_AC_Switch', false) SetUserParam(0, 'Test_AC_Switch', false) elseif cmd == "mode" then elseif cmd == "fan" then elseif cmd == "vane" then elseif cmd == 'temp' then end elseif string.lower(parts[3]) == "blind" then -- Topic: cbus/write/blind/grp/action|position net = 0; app = 56 grp = tonumber(parts[4]) cmd = string.lower(parts[5]) if cmd == 'position' then pos = tonumber(payload) -- See if position is numeric value if pos ~= nil then -- Map 0-100 to 0-255 cbus_pos = map(pos, 0, 100, 0, 255) SetCBusLevel(net, app, grp, cbus_pos, 0) end elseif cmd == "action" then action = string.lower(payload) if action == "open" then SetCBusLevel(net, app, grp, 255, 0) -- 255% elseif action == "close" then SetCBusLevel(net, app, grp, 0, 0) -- 0% elseif action == "stop" then SetCBusLevel(net, app, grp, 5, 0) -- 2% elseif speed == "toggle" then SetCBusLevel(net, app, grp, 249, 0) -- 98% elseif speed == "toggle_open" then SetCBusLevel(net, app, grp, 252, 0) -- 99% elseif speed == "toggle_close" then SetCBusLevel(net, app, grp, 2, 0) -- 1% end end elseif string.lower(parts[3]) == "raw" then -- Topic: cbus/write/raw/net/app/grp/switch|ramp net = 0; app = tonumber(parts[5]) grp = tonumber(parts[6]) cmd = string.lower(parts[7]) if cmd == 'switch' then if string.upper(payload) == "ON" then SetCBusLevel(net, app, grp, 255, 0) elseif string.upper(payload) == "OFF" then SetCBusLevel(net, app, grp, 0, 0) end elseif cmd == "ramp" then if string.upper(payload) == "ON" then SetCBusLevel(net, app, grp, 255, 0) elseif string.upper(payload) == "OFF" then SetCBusLevel(net, app, grp, 0, 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(net, app, grp, num, ramp[2]) else SetCBusLevel(net, app, grp, num, 0) end end end end end end client:login_set(mqtt_username, mqtt_password) client:connect(mqtt_broker) client:loop_forever()
It was the mosquitto lib causing the issue loop_forever is a blocking function tried loop_start had no luck either ended up setting up a loop and calling loop(0) then adding incomming UserParm data to a queue thats checked each cycle also renamed grp variables
Ages ago I managed to get loop_start() to work, but ended up with weird stability issues with the script crashing. So you're now using the same approach I use with https://github.com/autoSteve/acMqtt. Do an infinite loop that calls MQTT client:loop() as well as listening on a socket to enable bi-directional CBus/Mosquitto.
A comment about your code, @Jagomeister. The variable 'grp' is a special global variable on an AC, so you are clobbering this by not declaring your grp variables as local. This may be okay, but certainly won't be if you want to use some of the 'behind the scenes' Logic Machine stuff, like grp.gettags(alias). I always use 'group' instead of 'grp'. The AC is based on Logic Machine, so you might find this link handy. https://kb.logicmachine.net/libraries/lua/ An example utilising 'grp' in two ways: Code: if event.getvalue() == 255 then for _, tag in ipairs(grp.gettags(event.dst)) do -- Get the keywords for the object that raised the event parts = tag:split('=') if parts[2] ~= nil then if parts[1] == 'up' then up = parts[2] break end end end grp.write(event.dst, 0) -- Set the level of the group that raised the event to zero PulseCBusLevel('Local Network', 'Lighting', up, 255, 0, 3, 0) end
And another handy one-liner. This will convert an 'event.dst' alias like '0/56/123' in an event script into a table of numbers (three for lighting/user param, and four for measurement app): Code: local addr = load("return {"..event.dst:gsub('/',',').."}")() Then you could use statements like objectName = GetCBusGroupTag(addr[1], addr[2], addr[3]), and avoid using variables like net, app, group at all.
Going back to post zero, what's your AC set up, BTW, @Jagomeister? Sounds like Airtopia. If so, my acMqtt script supports Airtopia to a HomeAssistant climate device...
did a full rewrite. Running pretty good now setting timeoout 250 blocks long enough not to hammer cpu but is still pretty responsive so far its running stable. I have Mitsubishi Electric splits each has a Inteses ME-AC-MBS-1 connected that bridges it to the modbus. Was that or the wifi adapters and I decided that way. This was close on 5 years ago only just had time to actually config them. for the last 4.5 years I had been running a custom version of cgate web that I had rewritten to support more then I network. As I have cbus in more then 1 building, But it was not great and had issue if server ever went down. then there is a nodered that talks to mqtt (cbus and native devices) and s7 (siemen logos) this them gives the interface to control all lighting thats not my house. Code: while true do -- Run mosquitto and collect messages client:loop(250) -- Process any message that are in the queue while MQTT2CBUS.queue.last >= MQTT2CBUS.queue.first do -- items in the queue if MQTT2CBUS.logging then log('MQTT2CBUS Queue Size:', ((MQTT2CBUS.queue.last + 1) - MQTT2CBUS.queue.first), MQTT2CBUS.queue.first, MQTT2CBUS.queue.last) end local data = MQTT2CBUS.queue.data[MQTT2CBUS.queue.first] MQTT2CBUS.queue.data[MQTT2CBUS.queue.first] = nil MQTT2CBUS.queue.first = MQTT2CBUS.queue.first + 1 if MQTT2CBUS.logging then log('MQTT2CBUS Queue Data:', data) end -- Process the data if data.class == 'light' then MQTT2CBUS.light(data.addr.grp, data.command, data.payload) elseif data.class == 'fan' then MQTT2CBUS.fan(data.addr.grp, data.command, data.payload) elseif data.class == 'blind' then MQTT2CBUS.blind(data.addr.grp, data.command, data.payload) elseif data.class == 'hvac' then MQTT2CBUS.hvac(data.addr.grp, data.command, data.payload) elseif data.class == 'switch' then MQTT2CBUS.switch(data.addr.grp, data.command, data.payload) elseif data.class == 'raw' then MQTT2CBUS.raw(data.addr, data.command, data.payload) else error('MQTT2CBUS: Unknown class', data.class) end end
Nicely done. Here's a trick I use to get max performance and cut down the number of lines. Untested, but should work instead of the if / elseif / elseif / elseif... Functions can be assigned to variables in LUA, and table lookup is blazingly fast. The data.class ~= 'raw' and/or bit changes from using data.addr.grp to data.addr for raw calls. Code: local opts = { light = MQTT2CBUS.light, fan = MQTT2CBUS.fan, blind = MQTT2CBUS.blind, hvac = MQTT2CBUS.hvac, switch = MQTT2CBUS.switch, raw = MQTT2CBUS.raw, } -- Process the data if opts[data.class] ~= nil then opts[data.class](data.class ~= 'raw' and data.addr.grp or data.addr, data.command, data.payload) else error('MQTT2CBUS: Unknown class', data.class) end And depending on the structure of MQTT2CBUS, this might even be able to be cut further to just a one-liner: Code: -- Process the data if MQTT2CBUS[data.class] ~= nil then MQTT2CBUS[data.class](data.class ~= 'raw' and data.addr.grp or data.addr, data.command, data.payload) else error('MQTT2CBUS: Unknown class', data.class) end