RadonEye BLE Interface

@ceandre The dash in the REkey should be fine. I must have overlooked the the replace “:”,"-" portion and thought it was stripping the : out completely. That was my bad.

Also I had the ‘clean_session=True’ in there as I was throwing args at it to fix the connection issue before stumbling on the random number generation in the client id.

Thank you again for diving right in.

2 Likes

Hi all,

thanks to the first post getting all the information about the BT-Proto of the radoneye sensor. I have spent some time to programm a custom_sensor.h with radoeye for esp32 boards using esphome-Software. It is not done yet, but maybe a good start.

Best Michi

‘file: my_custom_sensor.h’

#include "esphome.h"
#include "BLEDevice.h"
//#include "BLEScan.h"


// The remote service we wish to connect to.
static BLEUUID serviceUUID("00001523-1212-efde-1523-785feabcd123");
//service = dev.getServiceByUUID(btle.UUID('00001523-1212-efde-1523-785feabcd123'))
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("00001525-1212-efde-1523-785feabcd123");
static BLEUUID    char24UUID("00001524-1212-efde-1523-785feabcd123");
//character = dev.getCharacteristics(startHnd=1, endHnd=0xFFFF, uuid=btle.UUID('00001525-1212-efde-1523-785feabcd123'))

static union { char c[4]; uint32_t b; float f; } radonval;
static union { char c[2]; uint16_t b; } pulsval;
static float radonnow; 
static float radonday;
static float radonmonth;
static int puls;
static int puls10; 

static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLERemoteCharacteristic* p2RemoteCharacteristic;
static BLERemoteService* pRemoteService;
static BLEAdvertisedDevice* myDevice;
static String My_BLE_Address = "C1:96:0D:53:E9:A6";
//radon1 DA:B9:2C:5A:A9:75
//radon2 C1:96:0D:53:E9:A6
static BLEAddress *Server_BLE_Address;
static String Scanned_BLE_Address;
static boolean foundDevice = false; // Flag verifying if we found our device


static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    ESP_LOGD("custom","Callback");
    //ESP_LOGD("custom","Notify callback for characteristic %s", pBLERemoteCharacteristic->getUUID().toString().c_str());
    //ESP_LOGD("custom"," of data length %i", length);
    //ESP_LOGD("custom","data: %s",(char*)pData);
    // Read the value of the characteristic.
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
       ESP_LOGD("custom","onConnect");
  }

  void onDisconnect(BLEClient* pclient) {
    connected = false;
    ESP_LOGD("custom","onDisconnect");
  }
};

bool connectToServer() {
    ESP_LOGD("custom","Forming a connection to %s", myDevice->getAddress().toString().c_str());
    
    BLEClient*  pClient  = BLEDevice::createClient();
    ESP_LOGD("custom"," - Created client");

    pClient->setClientCallbacks(new MyClientCallback());
    //myDevice->setAddress(BLEAddress("DA:B9:2C:5A:A9:75"))
    // Connect to the remote BLE Server.
    pClient->connect(myDevice);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
    ESP_LOGD("custom"," - Connected to server");
   
    // Obtain a reference to the service we are after in the remote BLE server.
    //BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    pRemoteService = pClient->getService(serviceUUID);
    if (pRemoteService == nullptr) {
      ESP_LOGD("custom","Failed to find our service UUID: %s", serviceUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    ESP_LOGD("custom"," - Found our service");

    // Obtain a reference to the characteristic in the service of the remote BLE server.
    pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr) {
      ESP_LOGD("custom","Failed to find our characteristic UUID: %s", charUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    ESP_LOGD("custom"," - Found our 1525 characteristic");
	
    // Obtain a reference to the characteristic to write to in the service of the remote BLE server.
    //dev.writeCharacteristic(11,b'\x50')
    p2RemoteCharacteristic = pRemoteService->getCharacteristic(char24UUID);
    if (p2RemoteCharacteristic == nullptr) {
      ESP_LOGD("custom","Failed to find our characteristic 1524 UUID: %s", charUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    if(p2RemoteCharacteristic->canWrite()) {
      p2RemoteCharacteristic->writeValue(0x50);
      ESP_LOGD("custom","write Value 0x50");
    }

    if(pRemoteCharacteristic->canNotify())
      pRemoteCharacteristic->registerForNotify(notifyCallback);

    connected = true;
	return true;
}
/**
 * Scan for BLE servers and find the first one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    //ESP_LOGD("custom","BLE Advertised Device found");

      if(foundDevice == false){   // Flag verifying if we found our device
         Server_BLE_Address = new BLEAddress(advertisedDevice.getAddress()); // Update Server_BLE_Address if we didn't
         Scanned_BLE_Address = Server_BLE_Address->toString().c_str();
         if(Scanned_BLE_Address == My_BLE_Address) // Compares the scanned adress to what we are looking for
        {
           foundDevice = true; // Found our device
           ESP_LOGD("custom","Found my MAC ");
         }      
     }
    // We have found a device, let us now see if it contains the service we are looking for.
    //foundDevice == true &&
    if ( advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {

      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = true;

    } // Found our server
  } // onResult
}; // MyAdvertisedDeviceCallbacks


class MyCustomSensor : public PollingComponent, public Sensor {
 public:
   Sensor *radon_now = new Sensor();
   Sensor *radon_day = new Sensor();
   Sensor *radon_month = new Sensor();
  // constructor
  MyCustomSensor() : PollingComponent(300000) {}

  void setup() override {
    // This will be called by App.setup()
	  Serial.begin(115200);
	  ESP_LOGD("custom","Starting BLE Client for radoneye sensor.");
	  BLEDevice::init("");

	  // Retrieve a Scanner and set the callback we want to use to be informed when we
	  // have detected a new device.  Specify that we want active scanning and start the
	  // scan to run for 5 seconds.
	  BLEScan* pBLEScan = BLEDevice::getScan();
	  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
	  pBLEScan->setInterval(1349);
	  pBLEScan->setWindow(449);
	  pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
	  pBLEScan->start(5, false);
  }
  
  void update() override {
	  // This will be called every "update_interval" milliseconds.
	  // If the flag "doConnect" is true then we have scanned for and found the desired
	  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are 
	  // connected we set the connected flag to be true.
	  if (doConnect == true) {
		if (connectToServer()) {
		  ESP_LOGD("custom","We are now connected to the BLE Server.");
		} else {
		  ESP_LOGD("custom","We have failed to connect to the server; there is nothin more we will do.");
		}
		doConnect = false;
	  }

	  // If we are connected to a peer BLE Server, update the characteristic each time we are reached
	  // with the current time since boot.
	  if (connected) {
		ESP_LOGD("custom","time %i", (int)millis()/1000);
        //p2RemoteCharacteristic = pRemoteService->getCharacteristic(char24UUID);
        if(p2RemoteCharacteristic->canWrite()) {
          p2RemoteCharacteristic->writeValue(0x50);
          ESP_LOGD("custom","Requested new values from sensor (write Value 0x50)");
        } else{
            ESP_LOGD("custom","Requested new values from sensor failed");
        }

        // Read the value of the characteristic.
        if(pRemoteCharacteristic->canRead()) {
            std::string value = pRemoteCharacteristic->readValue();
            ESP_LOGD("custom","read results");
            //ESP_LOGD("custom","byte0-7: %02X %02X %02X %02X %02X %02X %02X",value[0],value[1],value[2],value[3],value[4],value[5],value[6]);
            //z.B. 50 10 8F C2 05 40 7B
            radonval.c[0] = value[2];
            radonval.c[1] = value[3];
            radonval.c[2] = value[4];
            radonval.c[3] = value[5];
            radonnow = radonval.f * 37.0;
            radonval.c[0] = value[6];
            radonval.c[1] = value[7];
            radonval.c[2] = value[8];
            radonval.c[3] = value[9];
            radonday = radonval.f * 37.0;
            radonval.c[0] = value[10];
            radonval.c[1] = value[11];
            radonval.c[2] = value[12];
            radonval.c[3] = value[13];
            radonmonth = radonval.f * 37.0;
            pulsval.c[0] = value[14];
            pulsval.c[1] = value[15];
            puls = pulsval.b;
            pulsval.c[0] = value[16];
            pulsval.c[1] = value[17];
            puls10 = pulsval.b;
            //ESP_LOGD("custom","Radon %X", radonval.b);
            ESP_LOGD("custom","Radonnow %.0f Day %.0f Month %.0f Pulse %i Puls10min %i ", radonnow, radonday,radonmonth, puls, puls10);
        }
        //Publish Values to frontend
        radon_now->publish_state(radonnow);
        radon_day->publish_state(radonday);
        radon_month->publish_state(radonmonth);
        ESP_LOGD("custom","Publish new values to frontend");
		
		// Set the characteristic's value to be the array of bytes that is actually a string.
		//pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
	  }else if(doScan){
          ESP_LOGD("custom","Not connected anymore");
		  setup();
          //BLEDevice::getScan()->start(0);  // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino
	  }
  
  }
};

and here the radon1.yaml:

esphome:
  name: radon1
  platform: ESP32
  board: nodemcu-32s
  includes:
    - my_custom_sensor.h

wifi:
  ssid: "yourssid"
  password: "yourpasswd"

api:
    
logger:

ota:

sensor:
  - platform: custom
    lambda: |-
      auto my_radon = new MyCustomSensor();
      App.register_component(my_radon);
      return {my_radon->radon_now, my_radon->radon_day, my_radon->radon_month};
    sensors:
      - name: "Radon now"
        unit_of_measurement: Bq
        accuracy_decimals: 0
      - name: "Radon day"
        unit_of_measurement: Bq
        accuracy_decimals: 0
      - name: "Radon month"
        unit_of_measurement: Bq
        accuracy_decimals: 0
1 Like

Hi all, find attached the updated and cleaned version. It now connects directly to your BT-Mac address. You need to provide the adress in the h-file.

Best Michi

#include "esphome.h"
#include "BLEDevice.h"

/************************************************************/
//put here your MAC Adress from the radoneye sensor
static String My_BLE_Address = "c1:96:0d:53:e9:a6";
/************************************************************/
//radon1 da:b9:2c:5a:a9:75 lowercase!!!!


// The remote service we wish to connect to.
static BLEUUID serviceUUID("00001523-1212-efde-1523-785feabcd123");
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("00001525-1212-efde-1523-785feabcd123");
static BLEUUID    char24UUID("00001524-1212-efde-1523-785feabcd123");
static BLEUUID    char23UUID("00001523-1212-efde-1523-785feabcd123");

static union { char c[4]; uint32_t b; float f; } radonval;
static union { char c[2]; uint16_t b; } pulsval;
static float radonnow; 
static float radonday;
static float radonmonth;
static int puls;
static int puls10; 

static BLERemoteService* pRemoteService;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLERemoteCharacteristic* p2RemoteCharacteristic;
static BLEClient*  pClient;

class MyCustomSensor : public PollingComponent, public Sensor {
 public:
   Sensor *radon_now = new Sensor();
   Sensor *radon_day = new Sensor();
   Sensor *radon_month = new Sensor();
   Sensor *radon_puls = new Sensor();
   Sensor *radon_puls10 = new Sensor();
  // constructor
  MyCustomSensor() : PollingComponent(60000) {}

  void setup() override {
    // This will be called by App.setup()
	ESP_LOGD("custom","setup start");
	Serial.begin(115200);
	BLEDevice::init("");
    //Convert Mac Adress in correct format
    BLEAddress bleadr = BLEAddress(My_BLE_Address.c_str());//"c1:96:0d:53:e9:a6");
    ESP_LOGD("custom"," - setup connect to mac %s", My_BLE_Address.c_str());
    pClient  = BLEDevice::createClient();
    ESP_LOGD("custom"," - setup- Created client");
    // Connect to the Radoneye  BLE Server.
    pClient->connect(bleadr, BLE_ADDR_TYPE_RANDOM);
    if(pClient->isConnected()) ESP_LOGD("custom"," - setup- Connected to Radoneye");
    else  ESP_LOGD("custom"," - setup- Connect Failed!!!");
}
  
  void update() override {
    // This will be called every "update_interval" milliseconds.
    ESP_LOGD("custom","update - time %i", (int)millis()/1000);
    if(!pClient->isConnected()) setup();
    
    if(pClient->isConnected())
    {
        // Obtain a reference to the service we are after in the remote BLE server.
        pRemoteService = pClient->getService(serviceUUID);
        if (pRemoteService == nullptr) {
            ESP_LOGD("custom","Failed to find our service UUID: %s", serviceUUID.toString().c_str());
        }

        // Obtain a reference to the characteristic in the service of the remote BLE server.
        pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
        if (pRemoteCharacteristic == nullptr) {
            ESP_LOGD("custom","Failed to find our characteristic UUID: %s", charUUID.toString().c_str());
        }
        
        // Obtain a reference to the characteristic to write to in the service of the remote BLE server.
        p2RemoteCharacteristic = pRemoteService->getCharacteristic(char24UUID);
        if (p2RemoteCharacteristic == nullptr) {
            ESP_LOGD("custom","Failed to find our characteristic 1524 UUID: %s", charUUID.toString().c_str());
        }
        //write 0x50 to request new data from sensor
        if(p2RemoteCharacteristic->canWrite()) {
            p2RemoteCharacteristic->writeValue(0x50);
            ESP_LOGD("custom","write Value to our characteristic 1524 with 0x50");
        }
        if(pRemoteCharacteristic->canRead()) {
            std::string value = pRemoteCharacteristic->readValue();
            ESP_LOGD("custom","result bytes: %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X %02X%02X %02X%02X", value[0],value[1],value[2],value[3], value[4],value[5],value[6],value[7], value[8],value[9],value[10],value[11], value[12],value[13],value[14],value[15], value[16],value[17],value[18],value[19]);
            //on 0x50: 50100AD7 63406666 46400000 00000100 15000000
            //on 0x51: 510E0200 1D400000 0C4A0800 71595341 15000000 (Peakvalue12-15, Time since start 4-8)?
            //11tage 9h 37min 44s =985064 Peak 488 
            //30s entspricht Byte[4] +1 
            radonval.c[0] = value[2];
            radonval.c[1] = value[3];
            radonval.c[2] = value[4];
            radonval.c[3] = value[5];
            radonnow = radonval.f * 37.0;
            radonval.c[0] = value[6];
            radonval.c[1] = value[7];
            radonval.c[2] = value[8];
            radonval.c[3] = value[9];
            radonday = radonval.f * 37.0;
            radonval.c[0] = value[10];
            radonval.c[1] = value[11];
            radonval.c[2] = value[12];
            radonval.c[3] = value[13];
            radonmonth = radonval.f * 37.0;
            pulsval.c[0] = value[14];
            pulsval.c[1] = value[15];
            puls = pulsval.b;
            pulsval.c[0] = value[16];
            pulsval.c[1] = value[17];
            puls10 = pulsval.b;
            ESP_LOGD("custom","Radonnow %.0f Day %.0f Month %.0f Pulse %i Puls10min %i ", radonnow, radonday, radonmonth, puls, puls10);
        }
        radon_now->publish_state(radonnow);
        radon_day->publish_state(radonday);
        radon_month->publish_state(radonmonth);
        radon_puls->publish_state(puls);
        radon_puls10->publish_state(puls10);
        ESP_LOGD("custom","Published new values to frontend");
    } else {
        ESP_LOGD("custom","Not connected to sensor");
    }
  }
};

3 Likes

Nice bit of code. Do you have a github link to a sample project so we can see all the parts in play?

Also:

  1. What’s a decent update/query frequency?
  2. Do you keep the connection open all the time? If so, what’s the impact on the sensor battery life?

thanks for your amazing esphome custom codes…

I don’t have radonEye, but I’ll buy one for your help.

I’ll attach with my esp32 nodes which already integrated with Xiaomi Sensors.

thx again

Hi all,

thanks for the radoneye component and the background info of the community.

Try to get it work but it seems to be that I am missing some bits.

I am runniung HA on a NUC / docker / hassio environment.

Managed to activate bluetooth on the NUC and got the MAC of RadonEye.

What I have done:

copied radon_reader.py to config/python_scripts in my HA config

chmod -X

tried to run

./config/python_scripts/radon_reader.py -a XX:YY:ZZ:AA:BB:CC -b -v -m -ma -ms 192.168.2.100 -mu my_username -mw mypassword’

in hassio terminal console.

Traceback (most recent call last):
File “./config/python_scripts/radon_reader.py”, line 12, in
import paho.mqtt.client as mqtt
ImportError: No module named paho.mqtt.client

seems to be the environment paho.mqtt.client is not accessible in terminal console within hassio.

tried than within HA as a script which I executed via developer_console / states for testing

script:

radon_readout_script:
alias: ‘radon readout script’
sequence:
- service: shell_command.read_radon_value

in configuration.yaml

sensor:

  • platform: mqtt
    name: ‘Radon Level’
    unit_of_measurement: ‘pCi/L’
    value_template: ‘{{ value_json.radonvalue }}’
    force_update: true
    state_topic: ‘enviroment/radoneye’

python_script:

shell_command:
read_radon_value: ‘python /config/python_scripts/radon_reader.py -a XX:YY:ZZ:AA:BB:CC -b -v -m -ma -ms 192.168.2.100 -mu my_username -mw mypassword’

tried also:
shell_command:
read_radon_value: ‘curl /config/python_scripts/radon_reader.py -a XX:YY:ZZ:AA:BB:CC -b -v -m -ma -ms 192.168.2.100 -mu my_username -mw mypassword’
read_radon_value1: curl /config/python_scripts/radon_reader.py -a XX:YY:ZZ:AA:BB:CC -b -v -m -ma -ms 192.168.2.100 -mu my_username -mw mypassword
read_radon_value2: ‘curl ./config/python_scripts/radon_reader.py -a XX:YY:ZZ:AA:BB:CC -b -v -m -ma -ms 192.168.2.100 -mu my_username -mw mypasswordl’
read_radon_value3: curl ./config/python_scripts/radon_reader.py -a XX:YY:ZZ:AA:BB:CC -b -v -m -ma -ms 192.168.2.100 -mu my_username -mw mypassword

What am I missing?

tnx

fregatte

" No module named paho.mqtt.client"
You need install paho-mqtt

Try:

sudo pip install paho-mqtt

Thanks for coming back that quickly; unfortunately within hassio I can`t install via sudo pip install paho-mqtt; I can do that just on the host but that does not help to get paho-mqtt available on HA hassio.

Is there a way to circumvent this requirement as I have a mosquito broker running as a hassio addon.

Or might it be possible to use your script within AppDaemon
(AppDaemon is a loosely coupled, multithreaded, sandboxed python execution
environment for writing automation apps for Home Assistant home automation
software. It also provides a configurable dashboard (HADashboard) suitable
for wall mounted tablets.)?

Thanks

fregatte

got it installed after reboot, but I think in this environment it is nonvolatile.

Also getting the next error:
File “./config/python_scripts/radon_reader.py”, line 14, in
from bluepy import btle
ImportError: No module named bluepy

tried to install:
sudo pip install bluepy

Installing collected packages: bluepy
Running setup.py install for bluepy … error
Complete output from command /usr/bin/python2 -u -c “import setuptools, tokenize;file=’/tmp/pip-install-KKKIjf/bluepy/setup.py’;f=getatt
r(tokenize, ‘open’, open)(file);code=f.read().replace(’\r\n’, ‘\n’);f.close();exec(compile(code, file, ‘exec’))” install --record /tmp/p
ip-record-K8ar4q/install-record.txt --single-version-externally-managed --compile:
running install
running build
running build_py
Working dir is /tmp/pip-install-KKKIjf/bluepy
execute make -C ./bluepy clean
error: [Errno 2] No such file or directory

So I think in hassio I have to go the appdaemon route. Therefore the script needs additional code but I am lacking knowledge to code this.

Thanks

fregatte

A big thank for sharing your code also from me. Works like a charm under hassio / docker on a nuc.

Sometimes I do have data spikes due to a weak bluetooth connection. Will try to deal with that with the HA filter component.

Many thanks (also to all others who shared their knowledge).

Happy :grinning: :grinning: :grinning: to have RadonEYE now integrated in HA.

fregatte

@fregatte
Hi!
I’m glad you managed to solve it.

On my script I consider a exception pikes bigger than 1000, and negative values… Are you getting pikes of what size in pCi/L ?

Hi Carlos,

in my last post I did not explicit mention that it for me works now with the ESP32 component (I just replied to “wettermann” who developed this add. solution).

Your code does unfortunately not work for me, not because of your code but because of my restricted environment “hassio”.

Even under “Appdaemon” the bluetooth hardware is not accessible (I guess that the docker wherein Appdaemon works does not know that there is bluetooth hw on the host).

So the spikes are with the ESP32 solution (for some reason on the monthly sensor).

Thanks to you all!!

fregatte

OMG! This is awesome and exactly what I was looking for, thank you so much for sharing @wettermann!

I’d like to get a second RD200 device and use one ESPHome device to monitor both of them so I’m trying to figure out how to pass in the btle MAC address to the my_custom_sensor.h file (instead of it being hard coded). Unfortunately, c is not my strong suit. :frowning: anyone know how to make that possible?

Thanks,
-Greg

This code keeps the connection open to the sensor. Using the mac-adr from the sensor as parameter you need to change this behaviour.
Alternative: Without understanding the code, the easy (also the hard) way is to double everything MyCustomSensor_1 and MyCustomSensor_2 and so on for every function and all global/static vars. Not so nice but straight forward…
If you go more into the code there are a lot of things you could share instead of double it.

Best Michi

Does anyone know what the puls and puls10 values represent? I’ve enabled them to push to home assistant but it’s just four bytes of data. The comments in the header file don’t seem to explain it very well.

Here’s what mine looks like for the last 30 minutes (since I enabled those):

I was sort of hoping it would represent uptime for the RD200…

Thanks,
-Greg

Guys, I have taken wettermann’s ESPHome code and published it to github: https://github.com/spikeygg/esphome_radoneye_rd200/blob/fe7284d05b19f84446f0bcfb21a45b67d4846dff/radoneye_rd200_sensor.h. I have pulled out the address so it can be passed in but now I’m trying to figure out how to solve the global variable problem.

All the unions, floats and ints defined outside the class get stomped when instantiating the second instance of RadonEye. I don’t know enough about C++ architecture to properly solve this problem.

Anyone know the right way to correct that?

Once we have this working you can use this ESPHome yaml to create a new radon monitor:

  - platform: custom
    lambda: |-
      auto radon1 = new RadonEye("ff:ff:ff:ff:ff:ff");
      App.register_component(radon1);
      return {radon1->radon_10min, radon1->radon_day, radon1->radon_month};
    sensors:
      - name: "Radon1"
        accuracy_decimals: 2
        icon: mdi:radioactive
        unit_of_measurement: pCi/L
      - name: "Radon1 day"
        accuracy_decimals: 2
        icon: mdi:radioactive
        unit_of_measurement: pCi/L
      - name: "Radon1 month"
        accuracy_decimals: 2
        icon: mdi:radioactive
        unit_of_measurement: pCi/L

Thanks,
-Greg

Been measuring this for the last month or so and my values were well over the limit so I had a vent installed last week. Here’s the result so far:

I’m glad I found this thread. :smiley:

just copy all the variables into the private section of the the RadonEye-class and remove static. I am coming from the early embedded programming world. memory management was bad at that time, so we always used global vars with static…

Best M

private:
   union { char c[4]; uint32_t b; float f; } radonval;
   union { char c[2]; uint16_t b; } pulsval;
   float radonnow;
   float radonday;
   float radonmonth;
   int puls;
   int puls10;
   BLERemoteService* pRemoteService;
   BLERemoteCharacteristic* pRemoteCharacteristic;
   BLERemoteCharacteristic* p2RemoteCharacteristic;
   BLEClient*  pClient;

I wanted to drop a line here to thank everyone for the work done in getting the RadonEye connected to Home Assistant. I’ve been wanting a radon sensor for a few months and I came across an article on Reddit about radon sensor they pointed me to this post. Shortly after Amazon had a lightning deal on the RadonEye for $130 that was too good to pass up. I had an extra RPi4 laying around that would be perfect for this project. A few weeks later I finally got around to plugging in the RadonEye and setting it up in the basement. After a few minutes I logged in to the RadonEye app and checked the sensor to see if it was working. It reported 43 pCi/l which seemed high at the time but I couldn’t remember. Once I got on to my computer I checked the EPA recommended limits and was pretty floored. We have an active radon mitigation system in our house that was installed when we bought it 10+ years ago. I went back down to the basement and checked the pressure reading on the radon system and it was not pulling a vacuum which means it likely was an broken radon fan.

Before I could start any repairs I needed accurate data so I began the process of getting the RadonEye in to Home Assistant and logged into InfluxDB with graphing being done by Grafana. It should have only taken me about two hours to get everything operational (I had not finished setting up my long term storage in InfluxDB). It ended up taking me 8 hours or so with 4 of them due to my troubleshooting of my install that all stemmed from me misspelling “environmental”. sigh

The next day I was able to find a local shop that had a radon fan that fit my 4" pipes so another $140 later I had my radon mitigation system completely operational again. I can’t imagine what a local radon company would have charged me to do this repair. When I removed the old fan I found the culprit for it no longer working. It appears that a squirll fell down the exhaust pip and died in the fan. The skull and bones were all still in there. Fun fact, most radon mitigation installers don’t install a simple wire mesh at the top of the stack to prevent debris from entering the exhaust. No idea why. As you can see from the plot below, it’s now at 3.2 and still dropping.

So again, thanks to everyone who’s worked on this project and contributed information to this thread. I had to dive into more details than found in this thread so to help give back to the community I documented the installation process for others to follow.

# Prerequisits on Home Assistant
Install MQTT broker (Mosquitto)
Create new HA user named radon
Add radon user to ACL for Mosquitto at share/mosquitto/accesscontrollist
   user radon
   topic readwrite environment/RADONEYE/#

# Prerequisits on the RPi4
sudo apt-get install git
sudo mkdir /data
sudo chmod 777 /data
cd /data
git clone https://github.com/ceandre/radonreader.git
sudo pip install paho-mqtt
sudo apt-get install python-pip libglib2.0-dev
sudo pip install bluepy

# Find MAC Address of RadonEye
bluetoothctl
--> scan on
Look for something like FR:R20:SN####
--> scan off

# Edit radon_reader.py to lower QoS to 0
vi radon_reader.py

# Back in Home Assistant
Add a new sensor to configuration.yaml
  - platform: mqtt
    name: "Radon Level"
    unique_id: "RadonEyeXXXXXX"  # <-- last three octets of the MAC 
    unit_of_measurement: "pCi/L"
    value_template: "{{ value_json.radonvalue }}"
    force_update: true
    state_topic: "environment/RADONEYE/XX-XX-XX"  # <-- last three octets of the MAC
    expire_after: 1200		# If no response in 20 mins mark the sensor as Unavailible
    icon: mdi:radioactive
    qos: 0

# Run radon_reader.py to check for the readings to come back properly
python /data/radonreader/radon_reader.py -a EF:EE:93:XX:XX:XX -v

# Then push the results via MQTT to HA
python /data/radonreader/radon_reader.py -a EF:EE:93:XX:XX:XX --mqtt -ma -ms 10.0.0.XX -mp 1883 -mu radon -mw XXXXXX -v

# Create a cron tab to have the program run every 5 minutes
crontab -e
*/5 * * * * python /data/radonreader/radon_reader.py -a EF:EE:93:XX:XX:XX --mqtt -ma -ms 10.0.0.XX -mp 1883 -mu radon -mw XXXXXX

# Check log files to make sure CRON is doing its job
grep CRON /var/log/syslog

# If there are no errors in the syslog then you're all done. 
# You can ignore the "No MTA installed" errors. If you want to get rid of them add MAILTO="" at the top of your crontab file.
# Recommend you archive the data from HA using InfluxDB and display with Grafana.
1 Like

What does this actually measure?