This is a server that implements a custom Ambient Light Sensor for adapting monitor brightness with the Lunar macOS app.
- Python 3.6+
# Installs dependencies and runs the server
make
# Runs the server without installing dependencies
make run
# If IPv6 is not available use HOST
make run HOST=0.0.0.0
# Listen on another port using the PORT variable
make run PORT=8080
The file lunarsensor.py
contains a server that reads lux values using the read_lux()
function at the bottom of the file.
Your actual sensor reading logic should be written in that function.
- Check if one-shot lux reading works
❯ curl lunarsensor.local/sensor/ambient_light
{"id":"sensor-ambient_light", "state":"0 lx", "value":0.000000}
- Check if the EventSource is sending lux values every 2 seconds
❯ curl -N lunarsensor.local/events
event: state
data: {"id": "sensor-ambient_light", "state": "400.0 lx", "value": 400.0}
event: state
data: {"id": "sensor-ambient_light", "state": "400.0 lx", "value": 400.0}
event: state
data: {"id": "sensor-ambient_light", "state": "400.0 lx", "value": 400.0}
An easily installable addon that runs the server on port 8899 and polls a sensor entity for lux values.
2. Reading from a BH1750 I²C sensor
pip3 install adafruit-circuitpython-bh1750
# Do the sensor reading logic below
import board
import adafruit_bh1750
i2c = board.I2C()
sensor = adafruit_bh1750.BH1750(i2c)
def dynamic_adjust_resolution(lux):
if lux > 300:
sensor.resolution = adafruit_bh1750.Resolution.LOW
elif lux > 20:
sensor.resolution = adafruit_bh1750.Resolution.MEDIUM
else:
sensor.resolution = adafruit_bh1750.Resolution.HIGH
async def read_lux():
lux = sensor.lux
dynamic_adjust_resolution(lux)
return lux
3. Reading from a VEML7700 I²C sensor
pip3 install adafruit-circuitpython-veml7700
# Do the sensor reading logic below
import board
import adafruit_veml7700
i2c = board.I2C()
sensor = adafruit_veml7700.VEML7700(i2c)
def dynamic_adjust_resolution(lux):
if lux > 300:
sensor.light_integration_time = adafruit_veml7700.ALS_25MS
sensor.light_gain = adafruit_veml7700.ALS_GAIN_1_8
elif lux > 100:
sensor.light_integration_time = adafruit_veml7700.ALS_50MS
sensor.light_gain = adafruit_veml7700.ALS_GAIN_1_4
elif lux > 20:
sensor.light_integration_time = adafruit_veml7700.ALS_100MS
sensor.light_gain = adafruit_veml7700.ALS_GAIN_1
elif lux > 10:
sensor.light_integration_time = adafruit_veml7700.ALS_200MS
sensor.light_gain = adafruit_veml7700.ALS_GAIN_1
else:
sensor.light_integration_time = adafruit_veml7700.ALS_400MS
sensor.light_gain = adafruit_veml7700.ALS_GAIN_2
async def read_lux():
lux = sensor.lux
dynamic_adjust_resolution(lux)
return lux
4. Reading from a HomeAssistant lux sensor
# Do the sensor reading logic below
HOME_ASSISTANT_URL = "http://homeassistant.local:8123" # Replace with your HomeAssistant server URL
TOKEN = "your.jwt.token" # Replace with your long-lived HomeAssistant API token
SENSOR_ENTITY_ID = "sensor.living_room_ambient_light" # Replace with your sensor entity id
async def read_lux():
async with CLIENT.get(f"{HOME_ASSISTANT_URL}/api/states/{SENSOR_ENTITY_ID}", headers={"Authorization": f"Bearer {TOKEN}"}) as response:
sensor = await response.json()
if not json:
return None
return float(sensor["state"])
5. Reading from a Phillips Hue API motion sensor
import math
# Do the sensor reading logic below
HUE_API_URL = os.getenv("HUE_API_URL") # set env variable to: http://<bridge_ip_address>/api/<user_id>
HUE_SENSOR_ID = os.getenv("HUE_SENSOR_ID") # send GET request to $HUE_API_URL/sensors and search for "Hue ambient light sensor"
async def read_lux():
async with CLIENT.get(f"{HUE_API_URL}/sensors/{HUE_SENSOR_ID}") as response:
sensor = await response.json()
if not json:
return None
# Converting light level measured by sensor: 10000*log10(lux)+1
lightlevel = sensor['state']['lightlevel']
lux = lightlevel - 1
lux = lux / 10000
lux = math.pow(10, lux)
return float(lux)
Example for starting on localhost:8080
docker build -t lunarsensor .
docker run -p 127.0.0.1:8080:80 -d lunarsensor
Lunar expects to find the sensor at the lunarsensor.local
address by default.
This can be changed using the defaults
command on the Mac where Lunar is running.
There are three settings that affect where Lunar looks for the sensor:
sensorHostname
set by default tolunarsensor.local
sensorPort
set by default to80
sensorPathPrefix
set by default to/
For example, if you would like to have Lunar listen for sensor events at homeassistant.local:8123/lunar/events
you would run the following commands:
defaults write fyi.lunar.Lunar sensorHostname homeassistant.local
defaults write fyi.lunar.Lunar sensorPort 8123
defaults write fyi.lunar.Lunar sensorPathPrefix /lunar