Xiaomi Mi Flora Plant Sensor MQTT Client/Daemon

Set this up on RaspiZero and connected to a couple of devices with no issue. Tried to run the script to create the items and it throws this error:

Generating openHAB items. Copy to your configuration and modify as needed...
Traceback (most recent call last):
  File "/opt/miflora-mqtt-daemon/miflora-mqtt-daemon.py", line 245, in <module>
    flores_to_openhab_items(flores, reporting_mode)
  File "/opt/miflora-mqtt-daemon/miflora-mqtt-daemon.py", line 113, in flores_to_openhab_items
    print('\n'.join(items))
UnicodeEncodeError: 'ascii' codec can't encode character '\xb0' in position 1006: ordinal not in range(128)

I have recently bought one of these as well and getting the data through Bluetooth binding.does anybody know the unit of the different values provided by this device?
I mean moisture - is this percent (currently 41)?
How about fertility? What’s the maximum and the minimum value?
Thanks in advance

Temperatur is degree celsius. Humdidity is percent. Fertility is µs/cm, Sunlight i don’t know. And in addition i do not know the min and max values.

Alright!
Thanks a lot!

Got a batch of Bluetooth Sensors as well and wanted to connect them,
seems the Miflora MQTT Daemon is not compatible with Openhab 2.5 Embedded Moquette MQTT Broker, i get a

Broker doesn't permit MQTT empty client ID. Username: mqtt, channel

error
Seems unlike e.g. Mosquito Moquette doesnt generate ClientID’s when none is passed, and the Miflora MQTT Daemon is missing the config to set one, haven’t figured out yet how to include that

According to this post by @rlkoshak the internal mosquitto broker is not maintained at the moment and it is therefore suggested to use alternative external broker

I get the warning too, but it doesn’t matter. It’s just a hint and it still works.

Which version of the paho-mqtt package is installed?

Command to determine the version: pip3 show paho-mqtt

The 1.4.0 should be installed.

@Lionello_Marrelli Thanks for pointing that out, i wasn’t aware of that,
seems all the components i want to utilize are either abandonen, experimental or broken, would be great if something like that would also be indicated in the documentation…

@mas for me it doesnt seem to work, i don’t get any data from the Miflora Daemon into OH, my milight bridge works though without issue.
the pip3 show paho-mqtt
doesnt show anything for me, it runs a couple seconds but doesnt serve a result…
Note, i got all components on the same Raspbian installation running, don’t know if that makes a difference

I tried “teaching” the MQTT Daemon to transmit a clientID, but didnt have any luck yet, never worked with python before…

Does this mean that you have both the openhab internal mosquitto broker and the openhab mosquitto client binding installed and correctly working?

If this is so you have to

  1. uninstall the openhab internal mosquitto broker
  2. install the mosquitto broker on raspberry
  3. check if your openhab and milight bridge communicate
  4. test if miflora-mqtt-daemon sends mqtt messages to the new broker
  5. test if the things and items configuration for miflora are correct

If you wish more help, you need to be more specific and show your things and items configurations, possibly in a dedicated new thread.

Hi,

i use the internal Moquette Broker, i did not require the MQTT binding before, i just installed it for Miflora now.
The ESP based milight bridge utilizes the EspMilightHub new binding for milight limitlessLED and easybulb binding

I’ll give it a try with Mosquitto as Broker, seems to be better choice anyhow given the circumstance, will post an update once i tried it.

Hi Thomas,

As an openhab2 newb i have been trying to get the notifications to work for my miflower sensors and i used your posted code. Thanks for that.
I have everything up and running and im reading my sensor data perfect. I see them all on my sitemap.
I am however struggeling with the rule and i cant get the status check to be filled. it keeps giving a No Data message .
Im hoping you can help because im lost. Im not getting any errors except these when i’ve updated my CheckPlantStatusRules.rules:

Validation issues found in configuration model ‘CheckPlantStatus.rules’, using it anyway:
The field Tmp_CheckPlantStatusRules.PlantDataMap refers to the missing type Object
The field Tmp_CheckPlantStatusRules.PlantDataMap refers to the missing type Object
The field Tmp_CheckPlantStatusRules.PlantDataMap refers to the missing type Object
The field Tmp_CheckPlantStatusRules.PlantDataMap refers to the missing type Object.

This is how my items file is build:

//----------------------------------------------------------------------------------------------------------------------------------------------------
//
//    MiFlora sensor at Phlebodium- Fern Blue Star C4:7C:8D:67:BA:A6 
//
//    Required values
//        Temperature:    10,0 - 35,0°C
//        Conductivity:   350 - 2000µs/cm
//        Moisture:       20 - 55%
//
//----------------------------------------------------------------------------------------------------------------------------------------------------
Group FernBlueStar "Fern Blue Star livingroom" <weather_pollen> (PlantThings)
        
DateTime    FernBlueStar_Brightness     "Last update time Fern Blue Star [%1$ta %1$tR]" <time>          (FernBlueStar)                      {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.timestamp)]"}
Number      FernBlueStar_Brightness     "Light Intensity Fern Blue Star [%d Lux]"       <light>         (FernBlueStar, PlantBrightness)     {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.light)]"}
Number      FernBlueStar_Temperature    "Temperature Fern Blue Star [%.1f °C]"          <temperature>   (FernBlueStar, PlantTemperature)    {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.temperature)]"}
Number      FernBlueStar_Conductivity   "Soil Fertility Fern Blue Star [%d µS/cm]"      <lawnmower>     (FernBlueStar, PlantConductivity)   {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.conductivity)]"}
Number      FernBlueStar_Moisture       "Soil Moisture Fern Blue Star [%d %%]"          <rain>          (FernBlueStar, PlantMoisture)       {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.moisture)]"}
Number      FernBlueStar_BatteryLevel   "Battery level Fern Blue Star [%d %%]"          <battery>       (FernBlueStar, BatteryLevel)        {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.battery)]"}
Switch      FernBlueStar_Battery        "Battery Fern Blue Star [MAP(battery.map):%s]"  <battery>       (FernBlueStar, Batteries)           {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JS(battery2switch.js)]"}
Number      FernBlueStar_Status         "Fern Blue Star [MAP(plantstatus.map):%s]"      <status>        (FernBlueStar, PlantStatus)


my groups are made in a separate file called plants.item:

Group Plants "Plants" <vacation> (Context)
    
     Group PlantThings "Plants" <lawnmower> (Plants)
        
    Group PlantMonitoring "Plantdata" <lawnmower> (Plants)
        Group PlantMoisture     "Soil Moisture" <rain> (PlantMonitoring)
        Group PlantConductivity "Soil Fertility" <lawnmower> (PlantMonitoring)
        Group PlantTemperature  "Temperature" <temperature> (PlantMonitoring)
        Group PlantBrightness   "Light Intensity" <light> (PlantMonitoring)
        Group PlantStatus       "Status" <weather_pollen> (PlantMonitoring)

I used your rules file and modified with my naming:

//------------------------------------------------------------------------------------------------------------------------------------------------------------
//
//    Constants and variables
//
//------------------------------------------------------------------------------------------------------------------------------------------------------------

// Values are taken from the Miflora app
val Map<String, Double> PlantDataMap = newHashMap (
    "FernBlueStar_Moisture_Min"                -> 15.0, "FernBlueStar_Moisture_Max"               -> 60.0, "FernBlueStar_Conductivity_Min"               -> 350.0, "FernBlueStar_Conductivity_Max"               -> 2000.0
//    "GF_LR_DypsisLutescens_Moisture_Min"       -> 15.0, "GF_LR_DypsisLutescens_Moisture_Max"      -> 60.0, "GF_LR_DypsisLutescens_Conductivity_Min"      -> 350.0, "GF_LR_DypsisLutescens_Conductivity_Max"      -> 2000.0,
//   "GF_LR_PterisCretica_Moisture_Min"         -> 15.0, "GF_LR_PterisCretica_Moisture_Max"        -> 60.0, "GF_LR_PterisCretica_Conductivity_Min"        -> 350.0, "GF_LR_PterisCretica_Conductivity_Max"        -> 1500.0,
//    "GF_LR_PhlebodiumAureum_Moisture_Min"      -> 15.0, "GF_LR_PhlebodiumAureum_Moisture_Max"     -> 60.0, "GF_LR_PhlebodiumAureum_Conductivity_Min"     -> 350.0, "GF_LR_PhlebodiumAureum_Conductivity_Max"     -> 2000.0,
//    "GF_BR_CyperusAlternifolius_Moisture_Min"  -> 15.0, "GF_BR_CyperusAlternifolius_Moisture_Max" -> 60.0, "GF_BR_CyperusAlternifolius_Conductivity_Min" -> 350.0, "GF_BR_CyperusAlternifolius_Conductivity_Max" -> 2000.0,
//    "GF_BR_CalatheaZebrina_Moisture_Min"       -> 15.0, "GF_BR_CalatheaZebrina_Moisture_Max"      -> 65.0, "GF_BR_CalatheaZebrina_Conductivity_Min"      -> 350.0, "GF_BR_CalatheaZebrina_Conductivity_Max"      -> 1000.0
)

//------------------------------------------------------------------------------------------------------------------------------------------------------------
//
//    Rule: Check the plant status watering and fertilisation every hour and update the status flag
//
//------------------------------------------------------------------------------------------------------------------------------------------------------------
rule "Check Plant Status"
when
    Time cron "0 0 * ? * *" //every Hour

then
    
    PlantThings.members.forEach[ Plant |
    
        var ActualMoisture = PlantMoisture.members.filter[ pm | pm.name == Plant.name+"_Moisture"].head.state as DecimalType
        var ActualConductivity = PlantConductivity.members.filter[ pm | pm.name == Plant.name+"_Conductivity"].head.state as DecimalType
        var ActualStatus = 0

        if (ActualMoisture <= PlantDataMap.get(Plant.name+"_Moisture_Min")) {
            ActualStatus = ActualStatus + 1
        }

        if (ActualMoisture > PlantDataMap.get(Plant.name+"_Moisture_Max")) {
            ActualStatus = ActualStatus + 4
        }

        if (ActualConductivity <= PlantDataMap.get(Plant.name+"_Conductivity_Min")) {
            ActualStatus = ActualStatus + 2
        }

        if (ActualConductivity > PlantDataMap.get(Plant.name+"_Conductivity_Max")) {
            ActualStatus = ActualStatus + 8
        }

        PlantStatus.members.filter[ pm | pm.name == Plant.name+"_Status"].head.postUpdate(ActualStatus)

        logInfo("rule.CheckPlantStatus", "Update status of {} to {}", Plant.name, transform("MAP", "plantstatus.map", ActualStatus.toString))

    ]

end



and the transformation files are there as well (wont post them because they are basically the same and working.

im not sure if the validation issues are the problem but i’m kinda stuck at this point. I was hoping you could help out.
Thanks in advance.
I

I am not a java expert at all, but maybe you miss some import statements,
(this is based on a rule that I copied from this forum, using hashmaps)

import org.eclipse.smarthome.model.script.ScriptServiceUtil
import java.util.HashMap
import java.util.ArrayList
1 Like

Thanks Lionello for taking time too look into this.
Unfortunately i dont think thats it. Tried it though but had no luck with it.

Get the messages that those are never used once i added them to the rules file.

The import 'java.util.HashMap' is never used.
The import 'org.eclipse.smarthome.model.script.ScriptServiceUtil' is never used.
The import 'java.util.ArrayList' is never used.

To be honest, i reworked my whole plat staff, but lets see if we can fix the problem.

Lets us start from the beginning of the rule. You should add some log statements.

First should be directly after start of the loop. a simple

logInfo("Debugging", "{}", Plant.name.toString)

This should us give a list of all plants. And by the way while testing your cron statement could trigger a little more often :slight_smile:

Next log line after the var stetment

logInfo("Debugging", "Moisture: {}; Conductivity: {}", ActualMoisture, ActualConductivity)

And another question do you get soms log lines from the last logIbfo statement in the rule?

After these results we make some more steps :slight_smile:

Hi Thomas,

Great, all help is very welcome. This is wrecking my head.

I eliminated the cron for testing purposes and added a switch to trigger the rule. Gives me a bit more controle :slight_smile:

First of all, i added
import java.util.Map to my rules ( this got rid of the errors: The field Tmp_CheckPlantStatusRules.PlantDataMap refers to the missing type Object)

I then added the debugging code as you can see below.
I

rule "Check Plant Status"
when
   // Time cron "0 0 * ? * *" //every Hour
     Item testswitch changed to ON
then
    logInfo("Debugging", "{}", Plants.name.toString)

    PlantThings.members.forEach[ Plants |
                logInfo("step 1")
        var ActualMoisture = PlantMoisture.members.filter[ pm | pm.name == Plants.name+"_Moisture"].head.state as DecimalType
        var ActualConductivity = PlantConductivity.members.filter[ pm | pm.name == Plants.name+"_Conductivity"].head.state as DecimalType
        var ActualStatus = 0
        logInfo("Debugging", "Moisture: {}; Conductivity: {}", ActualMoisture, ActualConductivity)
        if (ActualMoisture <= PlantDataMap.get(Plants.name+"_Moisture_Min")) {

            ActualStatus = ActualStatus + 1
        }

When i have done that i see this in my logviewer:
2020-01-23 11:35:08.986 [INFO ] [pse.smarthome.model.script.Debugging] - Plants
2020-01-23 11:35:08.993 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘Check Plant Status’: index=1, size=1

thats it. nothing else happens. Im beginning to think it is not parsing the plants based on the group Plants since nothing else happen

My plant grouping looks like this. Could that be a problem?

Group Plants "Plants" <vacation> (Context)
    
     Group PlantThings "Plants" <lawnmower> (Plants)
        
    Group PlantMonitoring "Plantdata" <lawnmower> (PlantThings)
        Group PlantMoisture     "Soil Moisture" <rain> (PlantMonitoring)
        Group PlantConductivity "Soil Fertility" <lawnmower> (PlantMonitoring)
        Group PlantTemperature  "Temperature" <temperature> (PlantMonitoring)
        Group PlantBrightness   "Light Intensity" <light> (PlantMonitoring)
        Group PlantStatus       "Status" <weather_pollen> (PlantMonitoring)

Ok, The errors are ok, because we didi something wrong :-). Change the script as shown below.

rule "Check Plant Status"
when
   // Time cron "0 0 * ? * *" //every Hour
     Item testswitch changed to ON
then

    logInfo("Debugging", "Plant status update started")
    PlantThings.members.forEach[ Plants |
    logInfo("Debugging", "{}", Plants.name.toString)
        var ActualMoisture = PlantMoisture.members.filter[ pm | pm.name == Plants.name+"_Moisture"].head.state as DecimalType
        var ActualConductivity = PlantConductivity.members.filter[ pm | pm.name == Plants.name+"_Conductivity"].head.state as DecimalType
        var ActualStatus = 0
        logInfo("Debugging", "Moisture: {}; Conductivity: {}", ActualMoisture, ActualConductivity)
        if (ActualMoisture <= PlantDataMap.get(Plants.name+"_Moisture_Min")) {

            ActualStatus = ActualStatus + 1
        }
...

We should now se a line with “Plant status update started” and then more line with the plant names.

Adding the map was pretty good and how many plants are in the system?

Hi Thomas,

At the moment i have 1 plant. i am ready to add more however i wanted a working baseline before moving ahead.

I changed the script accordingly and now i am getting below messages

2020-01-23 19:59:38.264 [INFO ] [pse.smarthome.model.script.Debugging] - Plant status update started

2020-01-23 19:59:38.271 [INFO ] [pse.smarthome.model.script.Debugging] - PlantMonitoring

2020-01-23 19:59:38.283 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Check Plant Status': cannot invoke method public abstract org.eclipse.smarthome.core.types.State org.eclipse.smarthome.core.items.Item.getState() on null

HM, not so easy to find the problem. Ti find the problem i would suggest to minimize the rule by commenting out everything that is not needed. Our goal is to make the foreach loop running and then fill it with more code step by step.

Should look like this

rule "Check Plant Status"
when
Item testswitch changed to ON
then
 
    logInfo("Debugging", "Plant status update started")   
    PlantThings.members.forEach[ Plant |
    
        logInfo("Debugging", "{}", Plant.name.toString)
    
    ]

end

Another option is too comment the whole rule and use this snippet as a new one

Okay, i feel we are getting further.

i made some changes on several spots. first, i added the planthings group to the .items file. This was a hunch because i read somewhere that group hierarchy isn’t implicit. i also added a second plant for testing purposes.
so my items now looks like this:

Group FernBlueStar "Fern Blue Star livingroom" <weather_pollen> (Plants)
        
DateTime    FernBlueStar_Brightness     "Last update time Fern Blue Star [%1$ta %1$tR]" <time>          (FernBlueStar)                      {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.timestamp)]"}
Number      FernBlueStar_Brightness     "Light Intensity Fern Blue Star [%d Lux]"       <light>         (FernBlueStar, PlantBrightness, PlantThings)     {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.light)]"}
Number      FernBlueStar_Temperature    "Temperature Fern Blue Star [%.1f °C]"          <temperature>   (FernBlueStar, PlantTemperature, PlantThings)    {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.temperature)]"}
Number      FernBlueStar_Conductivity   "Soil Fertility Fern Blue Star [%d µS/cm]"      <lawnmower>     (FernBlueStar, PlantConductivity, PlantThings)   {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.conductivity)]"}
Number      FernBlueStar_Moisture       "Soil Moisture Fern Blue Star [%d %%]"          <rain>          (FernBlueStar, PlantMoisture, PlantThings)       {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.moisture)]"}
Number      FernBlueStar_BatteryLevel   "Battery level Fern Blue Star [%d %%]"          <battery>       (FernBlueStar, BatteryLevel, PlantThings)        {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.battery)]"}
Switch      FernBlueStar_Battery        "Battery Fern Blue Star [MAP(battery.map):%s]"  <battery>       (FernBlueStar, Batteries, PlantThings)           {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JS(battery2switch.js)]"}
Number      FernBlueStar_Status         "Fern Blue Star [MAP(plantstatus.map):%s]"      <status>        (FernBlueStar, PlantStatus, PlantThings)

after i have done that i used the follow rule file to test:

rule "Check Plant Status"
when
Item testswitch changed to ON
then
 
    logInfo("Debugging", "Plant status update started")   
    PlantThings.members.forEach[ Plants |
    
        logInfo("Debugging", "{}", Plants.name.toString)
    

//	var ActualMoisture = PlantMoisture.members.filter[ pm | pm.name == Plants.name+"_Moisture"].head.state as DecimalType
//        var ActualConductivity = PlantConductivity.members.filter[ pm | pm.name == Plants.name+"_Conductivity"].head.state as DecimalType
//        var ActualStatus = 0
//        logInfo("Debugging", "Moisture: {}; Conductivity: {}", ActualMoisture, ActualConductivity)
    ]

end

now runing the rules gives the follow log result:

2020-01-24 09:38:22.886 [INFO ] [pse.smarthome.model.script.Debugging] - Plant status update started

2020-01-24 09:38:22.893 [INFO ] [pse.smarthome.model.script.Debugging] - PlantMonitoring

2020-01-24 09:38:22.898 [INFO ] [pse.smarthome.model.script.Debugging] - FernBlueStar_Brightness

2020-01-24 09:38:22.903 [INFO ] [pse.smarthome.model.script.Debugging] - FernBlueStar_Temperature

2020-01-24 09:38:22.908 [INFO ] [pse.smarthome.model.script.Debugging] - FernBlueStar_Conductivity

2020-01-24 09:38:22.913 [INFO ] [pse.smarthome.model.script.Debugging] - FernBlueStar_Moisture

2020-01-24 09:38:22.918 [INFO ] [pse.smarthome.model.script.Debugging] - FernBlueStar_BatteryLevel

2020-01-24 09:38:22.924 [INFO ] [pse.smarthome.model.script.Debugging] - FernBlueStar_Battery

2020-01-24 09:38:22.928 [INFO ] [pse.smarthome.model.script.Debugging] - FernBlueStar_Status

2020-01-24 09:38:22.933 [INFO ] [pse.smarthome.model.script.Debugging] - plant2_Brightness

2020-01-24 09:38:22.937 [INFO ] [pse.smarthome.model.script.Debugging] - plant2_Temperature

2020-01-24 09:38:22.942 [INFO ] [pse.smarthome.model.script.Debugging] - plant2_Conductivity

2020-01-24 09:38:22.947 [INFO ] [pse.smarthome.model.script.Debugging] - plant2_Moisture

2020-01-24 09:38:22.953 [INFO ] [pse.smarthome.model.script.Debugging] - plant2_BatteryLevel

2020-01-24 09:38:22.958 [INFO ] [pse.smarthome.model.script.Debugging] - plant2_Battery

2020-01-24 09:38:22.962 [INFO ] [pse.smarthome.model.script.Debugging] - plant2_Status

I think this is what we should expect.

theni adjusted the rule file so one var is declared with the debug rule which i think should show that var.
.rule now looks like this:

rule "Check Plant Status"
when
Item testswitch changed to ON
then
 
    logInfo("Debugging", "Plant status update started")   
    PlantThings.members.forEach[ Plants |
    
        logInfo("Debugging", "{}", Plants.name.toString)
    

	var ActualMoisture = PlantMoisture.members.filter[ pm | pm.name == Plants.name+"_Moisture"].head.state as DecimalType
//        var ActualConductivity = PlantConductivity.members.filter[ pm | pm.name == Plants.name+"_Conductivity"].head.state as DecimalType
//        var ActualStatus = 0
        logInfo("Debugging", "Moisture: {}; Conductivity: {}", ActualMoisture, ActualConductivity)
    ]

end

running the rule like this gives an log error:

2020-01-24 09:41:26.769 [INFO ] [pse.smarthome.model.script.Debugging] - Plant status update started

2020-01-24 09:41:26.775 [INFO ] [pse.smarthome.model.script.Debugging] - PlantMonitoring

2020-01-24 09:41:26.789 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Check Plant Status': cannot invoke method public abstract org.eclipse.smarthome.core.types.State org.eclipse.smarthome.core.items.Item.getState() on null

So somehow the debugging doens’t show the plantsthings anymore and throws a new error.

one step further but not there :slight_smile:

Ok, the idea is iterate trough all plants, take the specific data and calculate the status.

Your group and item structure is not correct at the moment. It should be like this


Group PlantThings "Group that contains all plants"
Group PlantStatus "Group that contains all status items"
Group PlantMoisture "Group that contains all moisture items"
Group PlantConductivity "Group that contains all conductivity items"


Group FernBlueStar "Fern Blue Star livingroom" <weather_pollen> (PlantThings)
    DateTime    FernBlueStar_LastUpdate     "Last update time Fern Blue Star [%1$ta %1$tR]" <time>          (FernBlueStar)                      {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.timestamp)]"}
    Number      FernBlueStar_Brightness     "Light Intensity Fern Blue Star [%d Lux]"       <light>         (FernBlueStar)     {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.light)]"}
    Number      FernBlueStar_Temperature    "Temperature Fern Blue Star [%.1f °C]"          <temperature>   (FernBlueStar)    {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.temperature)]"}
    Number      FernBlueStar_Conductivity   "Soil Fertility Fern Blue Star [%d µS/cm]"      <lawnmower>     (FernBlueStar, PlantConductivity)   {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.conductivity)]"}
    Number      FernBlueStar_Moisture       "Soil Moisture Fern Blue Star [%d %%]"          <rain>          (FernBlueStar, PlantMoisture)       {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.moisture)]"}
    Number      FernBlueStar_BatteryLevel   "Battery level Fern Blue Star [%d %%]"          <battery>       (FernBlueStar, BatteryLevel)        {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JSONPATH($.battery)]"}
    Switch      FernBlueStar_Battery        "Battery Fern Blue Star [MAP(battery.map):%s]"  <battery>       (FernBlueStar, Batteries)           {mqtt="<[mosquitto:miflora/Fern-Blue_Star:state:JS(battery2switch.js)]"}
    Number      FernBlueStar_Status         "Fern Blue Star [MAP(plantstatus.map):%s]"      <status>        (FernBlueStar, PlantStatus)

rule "Check Plant Status"
when
Item testswitch changed to ON
then
 
    logInfo("Debugging", "Plant status update started")   
    PlantThings.members.forEach[ Plant |
    
        logInfo("Debugging", "{}", Plant.name.toString)
    
    ]

end

The groupstrcuture is now as follows.

PlantThings contains all Groups the define a Plant like FernBlueStar and FernBlueStar contains all items that belong to that specific plant. The Moisture, status and conductivity group contain all items of the type.

Now our loop is able to iterate trough the plants.

Please try it