Lambda help please

Hi all, I am struggling with the logic of this and can’t figure it out so I would appreciate any help, please.

I am trying to use a BH1750 light sensore to control the brightness of a light. The light brightness range is 0 to 1, the BH1750 state range is higher so I need a way to map this. Currently I have the following:

  - platform: bh1750
    name: "BH1750 Illuminance"
    id: bh1750_illuminance
    address: 0x23
    update_interval: 1s
    on_value:
      - light.turn_on:
          id: led_light
          brightness: !lambda |
            return id(bh1750_illuminance).state / (100.0);

But this still goes over 1 periodically. I’ve seen some examples using a map lambda approach, but can’t get that to work either.

Any suggestions would be very welcome. Thanks.

1 Like

I think the sensor outputs in lux, which is a linear scale and the sensor goes to 65K or so. The eye’s response to light isn’t linear but more logarithmic. So what exactly are you trying to do?

Thanks for your response, @neel-m

The BH1750 is to be used indoors only so I do not expect to need the full range available (I expect a lower top end) and I want to be able to map that to the 0.0 - 1.0 range. I saw this snippet posted somewhere but have not been able to get it to work yet.

    on_value:
      - light.turn_on:
          id: back_light
          brightness: !lambda |
             float dynamic_brightness = map(id(bh1750_illuminance).state, 0, 2000, 0.0, 1.0);
             return id(back_light).turn_on(brightness: dynamic_brightness);

It gives this error when I try to install it (just key part copied):

/config/esphome/input-office-tft.yaml: In lambda function:
/config/esphome/input-office-tft.yaml:612:34: error: 'brightness' was not declared in this scope
             return id(back_light).turn_on(brightness: dynamic_brightness);

I asked a friendly AI tool for assistance and that has helped:

- platform: bh1750
  name: "BH1750 Illuminance"
  id: bh1750_illuminance
  address: 0x23
  update_interval: 1s
  on_value:
    - light.turn_on:
        id: back_light
        brightness: !lambda |-
          // Get the current illuminance state
          float illuminance = id(bh1750_illuminance).state;

          // Define your input and output ranges
          float in_min = 0.0;
          float in_max = 2000.0; // Adjust this max illuminance based on your environment
          float out_min = 0.0;
          float out_max = 1.0;  // Brightness is between 0.0 and 1.0

          // Manual mapping calculation (linear interpolation)
          float brightness = out_min + (out_max - out_min) * ((illuminance - in_min) / (in_max - in_min));

          // Clamp the brightness value to ensure it's within the valid range [0.0, 1.0]
          brightness = std::max(0.0f, std::min(1.0f, brightness));

          return brightness;

This is the first time I’ve tried something like that and I am very impressed!

What is the BH1750 range in your home? The BH1750 may read up to 65,535 LUX, but that is blindness level. Direct sunlight would be 10,000 lux.

So if your BH1750 range is 0 to 2,000 lux indoors, map it thus:

fmin(map(id(bh1750_illuminance).state, 0, 2000, 0.0, 1.0), 1.0);

Enjoy, but don’t ever trust. You will get whatever shit out from “him”.

Your problem seems to be not mathematic but more personal experience.
You could map the sensor output simply using filter like calibrate_polynomial or calibrate_linear with method: exact.

Take a look at typical lux levels inside. Here is a typical table:
https://www.pranaair.com/us/blog/illuminance-levels-indoors-the-standard-lux-levels/

That says a likely upper level is 1000 lux, unless of course your sensor has a clear view of outside and you live in Arizona (where 10K lux would not be unreasonable).

If you do a linear translation you will likely be unhappy. So think more exactly of what you want the translation to be. What is the purpose of the led? Is it an indicator or is it trying to correct for the deficiency in light or something completely different?

Thank you, @stevemann, I shall try this.

Thanks again, useful information. The sensor is slightly recessed so I will need to work with observations based on real-world sampling. The LED is for a display panel so it only has to look ‘right’ for the purpose and I can easily tweak the output to achieve that.

Because of my day job, I trust but verify. I’ve done that for decades with humans and computers - and apply the same approach to AI.

In this case, the search took about 30 seconds, and I had it working within about 3 minutes total. It may not be the most elegant approach or the most efficient but it worked - and quickly.

I will now iterate from there - as I have a working version, I can now experiment, tweak and tune at my leisure.

It occurred to me that I use the history graph to track my outside illuminance. You could adapt this to your indoor sensor to see the lux range of your environment:

type: history-graph
entities:
  - entity: sensor.outside_illuminance
title: Outside Illuminance
hours_to_show: 24

Good approach!
And computers never hallucinate, 2+2 is always 4. That’s not valid with AI.

Thanks, yes, I have done that with the unit after installing the config above. It seems to be working perfectly, with approx 1,200 lux maximum light level today where it is mounted - on a very sunny day here in southern England.