This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Shelly - WiFi IoT-Devices
#21
(05.07.2022, 06:36)admin Wrote: Add before multiply function:
Code:
function onofftobool(payload)
  return tostring(payload):lower() == 'on'
end

Then modify mqtt_to_object_conv table:
Code:
mqtt_to_object_conv = {
  ['shellies/shellyplug-s-D9AA39/relay/0'] = onofftobool,
}
Thanks!
Reply
#22
(20.06.2022, 13:14)a455115 Wrote:
Code:
if not broker then
  broker = '127.0.0.1'

  function multiply(mult)
    return function(value)
      local num = tonumber(value)
      if num then
        return num * mult
      else
        return value
      end
    end
  end

  -- topic to object map
  mqtt_to_object = {
    ['shellies/shellyplug-s-D9AA39/relay/0'] = '32/1/1',
    ['shellies/shellyplug-s-D9AA39/temperature'] = '32/1/2',
  }

-- optional topic value conversion function
mqtt_to_object_conv = {
  ['shellies/shellyplug-s-D9AA39/relay/0'] = multiply(1),
   ['shellies/shellyplug-s-D9AA39/temperature'] = multiply(1),
}

  -- object to topic map
object_to_mqtt = {
   -- ['1/1/1'] = 'out/topic1',
   -- ['1/1/2'] = 'out/topic2',
  }

  datatypes = {}

  grp.sender = 'mq'
  require('socket')

  for addr, _ in pairs(object_to_mqtt) do
    local obj = grp.find(addr)
    if obj then
      datatypes[ addr ] = obj.datatype
    end
  end

  mclient = require('mosquitto').new()

  mclient.ON_CONNECT = function(res, ...)
    log('mqtt connect status', res, ...)

    if res then
      for topic, _ in pairs(mqtt_to_object) do
        mclient:subscribe(topic)
      end
    else
      mclient:disconnect()
    end
  end

  mclient.ON_MESSAGE = function(mid, topic, payload)
    local addr = mqtt_to_object[ topic ]
    if addr then
      local fn = mqtt_to_object_conv[ topic ]

      if fn then
        payload = fn(payload)
      end

      grp.write(addr, payload)
    end
  end

  mclient.ON_DISCONNECT = function(...)
    log('mqtt disconnect', ...)
    mclientfd = nil
  end

  function mconnect()
    local fd

    mclient:connect(broker)
    fd = mclient:socket()

    -- fd ref is valid
    if fd then
      mclientfd = fd
    end
  end

  mconnect()

  function publishvalue(event)
    -- message from us or client is not connected
    if event.sender == 'mq' or not mclientfd then
      return
    end

    local addr = event.dst
    local dpt = datatypes[ addr ]
    local topic = object_to_mqtt[ addr ]

    -- unknown object
    if not dpt or not topic then
      return
    end

    local value = busdatatype.decode(event.datahex, dpt)
    if value ~= nil then
      if type(value) == 'boolean' then
        value = value and 1 or 0
      end

      mclient:publish(topic, tostring(value))
    end
  end

  lbclient = require('localbus').new(1)
  lbclient:sethandler('groupwrite', publishvalue)

  lbclientfd = socket.fdmaskset(lbclient:getfd(), 'r')

  -- run timer every 5 seconds
  timer = require('timerfd').new(5)
  timerfd = socket.fdmaskset(timer:getfd(), 'r')
end

-- mqtt connected
if mclientfd then
  mclientfdset = socket.fdmaskset(mclientfd, mclient:want_write() and 'rw' or 'r')
  res, lbclientstat, timerstat, mclientstat =
      socket.selectfds(10, lbclientfd, timerfd, mclientfdset)
-- mqtt not connected
else
  res, lbclientstat, timerstat =
    socket.selectfds(10, lbclientfd, timerfd)
end

if mclientfd and mclientstat then
  if socket.fdmaskread(mclientstat) then
    mclient:loop_read()
  end

  if socket.fdmaskwrite(mclientstat) then
    mclient:loop_write()
  end
end

if lbclientstat then
  lbclient:step()
end

if timerstat then
  -- clear armed timer
  timer:read()

  if mclientfd then
    mclient:loop_misc()
  else
    mconnect()
  end
end
Hi admin,
How do I convert directly from the code to get a specific parameter from the Json it returns? For an example of a specific topic it returns json to me 

shellies/shellytrv-14B457E5C2C8/info
return
{"tmp": {"value":24.5,"units": "C","is_valid": true},"target_t":{"enabled":false,"value":22.0,"units":"C "},"temperature_offset":0.0,"bat":99}

I only want to extract from it:
target_t - value - 1/1/1
bat - 1/1/2
How can I extract only this with the above code?
Reply
#23
Use this (1/1/1 is battery object, 1/1/2 is target temperature value object):
Code:
mqtt_to_object = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = '1/1/1',
}

mqtt_to_object_conv = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = function(payload)
    local data = require('json').pdecode(payload)

    if type(data) == 'table' then
      grp.write('1/1/2', data.target_t.value)
      return data.bat
    end
  end
}
Reply
#24
(14.10.2022, 06:17)admin Wrote: Use this (1/1/1 is battery object, 1/1/2 is target temperature value object):
Code:
mqtt_to_object = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = '1/1/1',
}

mqtt_to_object_conv = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = function(payload)
    local data = require('json').pdecode(payload)

    if type(data) == 'table' then
      grp.write('1/1/2', data.target_t.value)
      return data.bat
    end
  end
}
Hi admin,

Data.target_t.value does not work. This is a log from data, and from data.thermostats, how should I get the target_t ? If I have many shelits for each, should I add it this way?
Code:
log (data)

["ps_mode"]
  * number: 0
["thermostats"]
  * table:
   [1]
    * table:
     ["tmp"]
      * table:
       ["is_valid"]
        * bool: true
       ["units"]
        * string: C
       ["value"]
        * number: 24.7
     ["pos"]
      * number: 50
     ["target_t"]
      * table:
       ["value_op"]
        * number: 8
       ["enabled"]
        * bool: false
       ["units"]
        * string: C
       ["value"]
        * number: 22
     ["window_open"]
      * bool: false
     ["boost_minutes"]
      * number: 0
     ["schedule"]
      * bool: false
     ["schedule_profile"]
      * number: 1
["fs_free"]
  * number: 59376
["wifi_sta"]
  * table:
   ["connected"]
    * bool: true
   ["rssi"]
    * number: -59
   ["ssid"]
    * string: Project Solutions
   ["ip"]
    * string: 10.0.5.105
["dbg_flags"]
  * number: 0
["mac"]
  * string: 14B457E5C2C8
["cloud"]
  * table:
   ["connected"]
    * bool: false
   ["enabled"]
    * bool: false
["time"]
  * string: 12:47
["update"]
  * table:
   ["new_version"]
    * string: 20220811-152343/v2.1.8@5afc928c
   ["old_version"]
    * string: 20220811-152343/v2.1.8@5afc928c
   ["status"]
    * string: unknown
   ["beta_version"]
    * userdata: NULL
   ["has_update"]
    * bool: false
["bat"]
  * table:
   ["voltage"]
    * number: 4.077
   ["value"]
    * number: 99
["unixtime"]
  * number: 1666000030
["ram_total"]
  * number: 97280
["fw_info"]
  * table:
   ["fw"]
    * string: 20220811-152343/v2.1.8@5afc928c
   ["device"]
    * string: shellytrv-14B457E5C2C8
["calibrated"]
  * bool: true
["uptime"]
  * number: 5326
["charger"]
  * bool: false
["ram_free"]
  * number: 22568
["actions_stats"]
  * table:
   ["skipped"]
    * number: 0
["serial"]
  * number: 0
["mqtt"]
  * table:
   ["connected"]
    * bool: true
["has_update"]
  * bool: false
["cfg_changed_cnt"]
  * number: 0
["fs_size"]
  * number: 65536


log(data.thermostats)

[1]
  * table:
   ["tmp"]
    * table:
     ["is_valid"]
      * bool: true
     ["units"]
      * string: C
     ["value"]
      * number: 24.7
   ["pos"]
    * number: 50
   ["target_t"]
    * table:
     ["value_op"]
      * number: 8
     ["enabled"]
      * bool: false
     ["units"]
      * string: C
     ["value"]
      * number: 22
   ["window_open"]
    * bool: false
   ["boost_minutes"]
    * number: 0
   ["schedule"]
    * bool: false
   ["schedule_profile"]
    * number: 1
Reply
#25
Try this:
Code:
mqtt_to_object = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = '1/1/1', -- battery level
  ['shellies/shellytrv-ABC/info'] = '2/1/1', -- battery level
  ['shellies/shellytrv-DEF/info'] = '3/1/1', -- battery level
}

function conv_therm(payload, target_t_addr)
  local data = require('json').pdecode(payload)

  if type(data) == 'table' then
    grp.write(target_t_addr, data.thermostats[1].target_t.value)
    return data.bat.value
  end
end

mqtt_to_object_conv = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = function(payload)
    return conv_therm(payload, '1/1/2') -- target temp
  end,
  ['shellies/shellytrv-ABC/info'] = function(payload)
    return conv_therm(payload, '2/1/2') -- target temp
  end,
  ['shellies/shellytrv-DEF/info'] = function(payload)
    return conv_therm(payload, '3/1/2') -- target temp
  end
}
Reply
#26
(17.10.2022, 10:00)admin Wrote: Try this:
Code:
mqtt_to_object = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = '1/1/1', -- battery level
  ['shellies/shellytrv-ABC/info'] = '2/1/1', -- battery level
  ['shellies/shellytrv-DEF/info'] = '3/1/1', -- battery level
}

function conv_therm(payload, target_t_addr)
  local data = require('json').pdecode(payload)

  if type(data) == 'table' then
    grp.write(target_t_addr, data.thermostats[1].target_t.value)
    return data.bat.value
  end
end

mqtt_to_object_conv = {
  ['shellies/shellytrv-14B457E5C2C8/info'] = function(payload)
    return conv_therm(payload, '1/1/2') -- target temp
  end,
  ['shellies/shellytrv-ABC/info'] = function(payload)
    return conv_therm(payload, '2/1/2') -- target temp
  end,
  ['shellies/shellytrv-DEF/info'] = function(payload)
    return conv_therm(payload, '3/1/2') -- target temp
  end
}

It works, thanks a lot!
Reply
#27
What is the maximum number of objects/devices Logic Machine can work with without problems? I want to hook up an apartment complex with shelly.
Reply
#28
It should handle several hundred devices.
Reply
#29
Hi, with the above code, you use broadcast topic "shellies". If I use a topic set in a device, eg a ShellyPluss1 (relay) eith add-on module, how can i sort the different values on the same topic?
I made this based on the above, but it only works on th ebinary status object, not temperature:

Code:
function conv_shellyRelay(payload, numberRel, target_s_addr)
  local data = require('json').pdecode(payload)  
  if type(data) == 'table' then
      grp.write(target_s_addr, data["params"][numberRel]["output"])
      log(data.bat.value)
      return data.bat.value
  end
end  
  
  
function conv_shellyTemp(payload, numberTemp, target_t_addr)
  local data = require('json').pdecode(payload)  
  if type(data) == 'table' then
      log(data["params"][numberTemp]["tC"])
        grp.write(target_t_addr, data["params"][numberTemp]["tC"])
    return data.bat.value
  end
end  

  -- topic to object map
  mqtt_to_object = {
    ['Shelly/events/rpc']         = '33/1/8', -- Temperature 1
    ['Shelly/events/rpc']         = '33/1/9',    -- Status relay
      ['Shelly/events/rpc']         = '33/1/10',-- Temperature 2
  }


  
  
mqtt_to_object_conv = {
      ['Shelly/events/rpc'] = function(payload)
   return conv_shellyTemp(payload,"temperature:100", '33/1/8')
  end,
    
      ['Shelly/events/rpc'] = function(payload)
   return conv_shellyTemp(payload,"temperature:101", '33/1/10')
  end,
    
    ['Shelly/events/rpc'] = function(payload)
   return conv_shellyRelay(payload,"switch:0", '33/1/9')
  end,    
    
}

  -- object to topic map
  object_to_mqtt = {
       ['33/1/7']     = 'Shelly/command/switch:0',
}
Reply
#30
You have duplicate keys in mqtt_to_object and mqtt_to_object_conv - this won't work. If multiple devices share the same topic then you need to manually implement logic for distinguishing what kind of data is received and which object must be updated.
Reply
#31
(03.01.2024, 10:19)admin Wrote: You have duplicate keys in mqtt_to_object and mqtt_to_object_conv - this won't work. If multiple devices share the same topic then you need to manually implement logic for distinguishing what kind of data is received and which object must be updated.

Ok, I was afraid of that. So i need to make logic that only reads topic once, and then sorts out the different values I'm looking for.

So I made a function to look up certain names in the table, is there any way to have the mqtt_to_object run without a GA attached? It will only run the first grp.write:
Code:
function conv_shellyTemp(payload)
  local data = require('json').pdecode(payload) 
      if type(data) == 'table' then
               function tableHasKey(table, prefix)
                        for key, _ in pairs(table) do
                      --log(key)
                        if string.match(key, prefix) then
                        return true
                        end
                        end
                        return false
                        end
         if tableHasKey(data["params"],"temperature") then
                   grp.write('33/1/8', data["params"]["temperature:100"]["tC"])
                grp.write('33/1/10', data["params"]["temperature:101"]["tC"])
       elseif
            tableHasKey(data["params"],"switch") then
                   grp.write('33/1/9', data["params"]["switch:0"]["output"])
          else
        log("no data")
        end
    end
  end
Reply
#32
(03.01.2024, 10:26)tomnord Wrote:
(03.01.2024, 10:19)admin Wrote: You have duplicate keys in mqtt_to_object and mqtt_to_object_conv - this won't work. If multiple devices share the same topic then you need to manually implement logic for distinguishing what kind of data is received and which object must be updated.

Ok, I was afraid of that. So i need to make logic that only reads topic once, and then sorts out the different values I'm looking for.

So I made a function to look up certain names in the table, is there any way to have the mqtt_to_object run without a GA attached? It will only run the first grp.write:
Code:
function conv_shellyTemp(payload)
  local data = require('json').pdecode(payload) 
      if type(data) == 'table' then
               function tableHasKey(table, prefix)
                        for key, _ in pairs(table) do
                      --log(key)
                        if string.match(key, prefix) then
                        return true
                        end
                        end
                        return false
                        end
         if tableHasKey(data["params"],"temperature") then
                   grp.write('33/1/8', data["params"]["temperature:100"]["tC"])
                grp.write('33/1/10', data["params"]["temperature:101"]["tC"])
       elseif
            tableHasKey(data["params"],"switch") then
                   grp.write('33/1/9', data["params"]["switch:0"]["output"])
          else
        log("no data")
        end
    end
  end

Figured it out, needed to activate status sending.
But I would lik eto be able to use the GetStatus function from, it seems to be a request that does not need a input.
https://shelly-api-docs.shelly.cloud/gen...us-example
Reply
#33
You can attach topic to a dummy object. If the conversion function does not return anything the grp.write won't happen anyway.

For HTTP requests search the forum, there are many examples that can be adapted.
Reply
#34
Code:
["thermostats"]
  * table:
   [1]
    * table:
     ["valve_min_percent"]
      * number: 0
     ["t_auto"]
      * table:
       ["enabled"]
        * bool: true
     ["target_t"]
      * table:
       ["units"]
        * string: C
       ["value_op"]
        * number: 8
       ["enabled"]
        * bool: true
       ["accelerated_heating"]
        * bool: true
       ["value"]
        * number: 7
     ["extra_pressure"]
      * bool: false
     ["calibration_correction"]
      * bool: true
     ["schedule_profile_names"]
      * table:
       [1]
        * string: Livingroom
       [2]
        * string: Livingroom 1
       [3]
        * string: Bedroom
       [4]
        * string: Bedroom 1
       [5]
        * string: Holiday
     ["schedule_profile"]
      * number: 1
     ["open_window_report"]
      * bool: false
     ["boost_minutes"]
      * number: 30
     ["valve_min_report"]
      * number: 1
     ["force_close"]
      * bool: false
     ["schedule_rules"]
      * table:
       [1]
        * string: 0600-0123456-23
       [2]
        * string: 2300-0123456-18
     ["ext_t"]
      * table:
       ["floor_heating"]
        * bool: false
       ["enabled"]
        * bool: true
     ["schedule"]
      * bool: false
     ["temperature_offset"]
      * number: 0

I am trying to write to the object "temperature_offset" but i can not get the syntax right. 
"'shellies/TRV_ROM1/settings/thermostats  . .. " 
It is stated as a parameter in the documentation for mqtt. 
https://shelly-api-docs.shelly.cloud/gen...rmostats-0

The http command is:

Code:
http://10.xxx.xxx.xxx/ext_t?temp=measured t°
Reply
#35
Try http://IP/settings/thermostats/0?temperature_offset=5
If this does not work then contact Shelly support.
Reply
#36
shellies/TRV_ROM1/thermostat/0/command/ext_t

This worked over MQTT. had to dig in the docu, TRV needed to be set as Floor_heating to use external value.


Quick question, can I have several MQTT scripts running, e.g one for each component? Or should I run all in one script?
Reply
#37
You can use multiple scripts but it makes more sense to handle everything in a single script.
Reply
#38
YEah, I guess. just thought it would be easier to auto generate GA's in separate scrips.
Reply


Forum Jump: