After a recent update my emulated_hue did not work anymore as before.
I made it work again but then found out that on each turn_on over emulated_hue
the light
- first starts with the previous brightness, (eg 10%)
- then switches instantly to 100%.
Over the dashboard everything is fine, only when I use the activation over
emulated_hue I see a instant “brightness” : 255 change after I “turn_on”.
I haven’t found any reports about this, most likely it’s only in my setup?
I was unable to find a workaround, so I started an AppDaemon script.
My idea was to block the call to brightness = 255, for a while (eg 4 secs),
directly after turn_on.
I haven’t figured out what component in my setup causes this behavior.
AppDaemon Script attached.
# filename: intercept.py
#
import appdaemon.plugins.hass.hassapi as hass
import datetime
import appdaemon.plugins.mqtt.mqttapi as mqtt
import time
# This class creates a virtual light which the only purpose to
# block any brightness calls for 4 seconds after a "turn_on" call.
#
# On my emulated_hue the lights are turned on with default brightness (eg. 10%)
# and directly afterwards the brighness is increased to 100%.
#
# This class intercepts the emulalted_hue calls, it creates a virtual light
# and forwards the calls to the real light(group). Calls after a turn_on
# are blocked for N seconds.
# Setup:
# Assume there is a "GroupKitchen" in configuration.yaml for emulated hue.
#
# [...]
# emulated_hue:
# listen_port: 80
# expose_by_default: false
# entities:
# light.groupkitchen: # <-- Your exact entity ID in Home Assistant
# name: "Kitchen" # <-- The name Alexa/Google Home will see
# hidden: false
# This needs to be changed to
# [...]
# emulated_hue:
# listen_port: 80
# expose_by_default: false
# entities:
# light.virtual_groupkitchen:
# name: "Kitchen"
# hidden: false
#
# In AppDaemon there is for every HUE light an intercepted light class.
# For example:
# - GroupKitchen it will have a module instance intercept_GroupKitchen.
#
# intercept_GroupKitchen:
# module: intercept
# class: InterceptHue
#
#
# Adding another light group is then done by adding new module intercept_xyz in
# apps.yaml
# GroupKichten has to match the real light.groupkitchen entity.
# Testing:
#
# Start with one Light "groupkitchen" and make the hue / appdaemon changes.
#
# - check in Settings->Developer Tools -> check Configuration etc..
# - reboot
# - check if virtual_groupkitchen is available in the emulated hue:
# http://<homeassistant.lan>/api/v2/lights
# - the homeassisant dashboard should have an entity virtual groupkitchen
#
# If the dashboard works for virtual_groupkitchen (on/off brightness),
# the hue lights needs to be re-discovered, because the uniqueids have changed.
# (even if the name stacs the same) Remove the old lights, then rediscover.
class InterceptHue(hass.Hass,mqtt.Mqtt):
def initialize(self):
self.bright_pct=100
self.blockInSec=4
self.blocktime=time.time()
# get the entity_id of the real light group
# get from our instance name "intercept_GroupKitchen" -> GroupKitchen (LightGroup)
# This name (entity_id or friendly_name) must macht exactly
self.lightGroup=self.name.replace('intercept_', '').lower()
self.entity_id_group = "light."+self.lightGroup
# create entity_id, which is always lower case
self.entity_id_virtual="light.virtual_"+self.lightGroup
self.log(f"creating entity_id : {self.entity_id_virtual} from entitiy_id : {self.entity_id_group}")
self.init_virtual_light_state()
# Note: here entity_id will be an array: [ "light.name"]
self.listen_event(self.my_call_back, "call_service", domain="light" )
# This filter will not work: service_data = { "entity_id" : [ self.entity_id_virtual ]})
# Note: here entity_id is a string: "light.name"
self.listen_event(self.turn_on_listener, "call_service", service="turn_on",
domain="light",
service_data={"entity_id": self.entity_id_virtual})
self.listen_event(self.turn_off_listener, "call_service", service="turn_off",
domaine="light",
service_data={"entity_id": self.entity_id_virtual})
self.listen_state(self.light_state_changed,self.entity_id_group)
def calc_percent(self,brightness_val):
brightness_pct = int(100.0* (float(brightness_val)/255.0))
return brightness_pct
def init_virtual_light_state(self):
self.log(f"ENTER: init_virtual_light_state entity_id: {self.entity_id_group}")
cur_state=self.get_state(self.entity_id_group)
if not (cur_state in ["on","off"]):
self.log(f"{self.entity_id_group} not initialized state={cur_state}")
cur_state="off"
stateAllTmpGroup = self.get_state(self.entity_id_group, attribute="all")
self.log(f"INFO: init_virtual_light_state {stateAllTmpGroup}")
brightness=stateAllTmpGroup.get("attributes").get("brightness")
if (brightness != None):
self.bright_pct=self.calc_percent(brightness)
# create a usual light entity with brightness:
attrib = { 'min_color_temp_kelvin': 2000, 'max_color_temp_kelvin': 6535,
'supported_color_modes': ['color_temp', 'rgb'],
'color_mode': 'color_temp',
'brightness': brightness,
'color_temp_kelvin': 2702,
'hs_color': [28.391, 65.659],
'rgb_color': [255, 167, 88],
'xy_color': [0.524, 0.388],
'supported_features': 40 }
self.log(f"INIT: {self.entity_id_virtual} {attrib}")
self.set_state(self.entity_id_virtual,state=cur_state,attributes=attrib)
def light_state_changed(self, entity, attribute, old, new, kwargs):
self.log(f"ENTER: light_state_changed {entity} from {old} to {new}")
attrs = self.get_state(entity, attribute="all")
#self.log(f"attrs: {attrs}")
virtual_state=self.get_state(self.entity_id_virtual)
if (virtual_state == new):
self.log(f"IGNORE: light_state_changed new {new} already in {self.entity_id_virtual}")
return
if (new == "on"):
self.turn_on(self.entity_id_virtual)
else:
self.turn_off(self.entity_id_virtual)
def set_virtual_light_state(self, new):
self.log(f"ENTER: set_virtual_light_state {self.entity_id_virtual} {new} brightness_pct={self.bright_pct}")
self.set_state(self.entity_id_virtual, state=new)
group_state=self.get_state(self.entity_id_group)
if (group_state == new):
self.log(f"IGNORE: set_virtual_light_state new {new} already in {self.entity_id_virtual}")
return
self.call_service("light/turn_"+new, entity_id=self.entity_id_group,
transition=0)
def my_call_back(self, event_name, data, **kwargs):
# event_name will be 'call_service'
# Extract details about the service call
domain = data.get("domain")
service = data.get("service")
service_data = data.get("service_data", {})
target_entity = service_data.get("entity_id")
lFound=False
self.log(f"ENTER: my_call_back : {domain}.{service} on {data} ")
if (type(target_entity) is list):
cnt=0
for element in target_entity:
#self.log(f"Array compare {target_entity}[{cnt}] == {element}")
if (element == self.entity_id_virtual):
target_entity=self.entity_id_virtual
lFound=True
#self.log(f"FOUND !")
break
if (type(target_entity) is str):
if (target_entity == self.entity_id_virtual):
lFound=True
if (lFound == False):
entity=service_data.get("entity_id")
self.log(f"IGNORE: my_call_back {entity} ")
return
#
brightness_pct = service_data.get("brightness_pct",None)
if (brightness_pct == None):
brightness_val = service_data.get("brightness",None)
if (brightness_val != None):
brightness_pct = self.calc_percent(brightness_val)
#self.log(f"INFO: my_call_back: {target_entity}.{service} on brightness_calc={brightness_pct} ")
if (service== "turn_on") and (brightness_pct != None):
if (time.time() < self.blocktime) :
self.log(f"BLOCK my_call_back: {target_entity}.{service} on brightness_ptc={brightness_pct} (keep : {self.bright_pct})")
else:
self.bright_pct=brightness_pct
self.log(f"OK my_call_back: {target_entity}.{service} on brightness_ptc={self.bright_pct} ")
self.turn_on(self.entity_id_group, brightness_pct=self.bright_pct)
return
self.log(f"INFO my_call_back: {target_entity}.{service} on brightness_pct={self.bright_pct} ")
if (service== "turn_on"):
self.log(f"OK: my_call_back: {service} {target_entity}")
self.blocktime=time.time()+self.blockInSec
self.set_virtual_light_state("on")
#self.turn_on(self.entity_id_virtual)
return
if (service== "turn_off"):
self.log(f"OK: my_call_back: {service} {target_entity}")
self.set_virtual_light_state("off")
#self.turn_off(self.entity_id_group)
return
self.log(f"IGNORE: my_call_back: {service} {target_entity} brightness_pct={brightness_pct}")
def turn_on_listener(self, event_name, data, kwargs):
self.log(f"ENTER: turn_on_listener {self.entity_id_virtual} {data} ")
self.set_virtual_light_state("on")
#self.turn_on(self.entity_id_virtual)
def turn_off_listener(self, event_name, data, kwargs):
self.log(f"ENTER: turn_off_listener {self.entity_id_virtual} {data}")
self.set_virtual_light_state("off")
#self.turn_off(self.entity_id_virtual)