I went away to work on something else for a bit and finally came back to this today.
After a bit of stress, I think I have everything playing nicely. I wanted to record what I did here for posterity in case anyone else wants to see the example (and so that if I ever have to recreate the config, there’s a backup here).
To recap: I have a 6-button keypad in wall and a Fanlinc in the fan linked together and also controlled via a Hub. I have a bunch of Scenes programmed in Insteon to keep the state of the two devices in sync.
The problem is that in HA I’m interacting with the light directly, which results in the keypad not matching the fan’s state. So, to keep the two in sync when controlling then from HA I created a set of automations to keep the state of the two devices in sync. There’s a 1-2 second lag before the keypad changes, but at least it catches up eventually.
#########
## These automations update the state of the keypad to match what the HA just did to the light
## Controls in HA interact with the light directly.
## These automations mirror the commands to the keypad so both change together.
#########
- alias: update keypad to match left light
trigger:
- platform: state
entity_id: light.fanlinc_X_light
from: 'off'
to: 'on'
action:
- service: light.turn_on
entity_id: light.keypad_with_dimmer_X_main
- alias: update keypad to match left light
trigger:
- platform: state
entity_id: light.fanlinc_X_light
from: 'on'
to: 'off'
action:
- service: light.turn_off
entity_id: light.keypad_with_dimmer_X_main
From the keypad, pressing ‘on’ and ‘off’ turns the light on the fan on and off quite nicely. The problem I ran into is that pressing the button on the keypad would send out a message that HA would intercept and update the keypad’s state, but the fanlinc as the responder either wasn’t sending out a message that it responded or HA just wasn’t getting it. The result was that using the keypad to turn the light on would show in HA as the keypad being on but it wasn’t showing that the actual light being controlled was on. So these automations update the state of the light in HA assuming that the light actually turned on when the keypad said it was pressed.
#########
## These automations update the state of the light in HA to match what the hardware just did
## The keypad is linked to light at hardware level, so when it is pressed the light turns on.
## Insteon reports to HA that the keypad was pressed, but doesn't update HA that the light changed
#########
- alias: update light to match left keypad being turned off
trigger:
- platform: state
entity_id: light.keypad_with_dimmer_X_main
from: 'off'
to: 'on'
condition:
# trigger only if light is not already on
# this is necessary otherwise the action here triggers the other automation below
- condition: state
entity_id: light.fanlinc_X_light
state: 'off'
action:
- service: python_script.set_entity_state
data:
entity_id: light.fanlinc_X_light
state: 'on'
brightness: 255
- alias: update light to match left keypad being turned on
trigger:
- platform: state
entity_id: light.keypad_with_dimmer_X_main
from: 'on'
to: 'off'
action:
- service: python_script.set_entity_state
data:
entity_id: light.fanlinc_X_light
state: 'off'
brightness: 0
For the actions in the above, I just wanted to update the state rather than trigger a light.turn_on since the light was already on. I also ran into an issue where I needed to specify the brightness otherwise the entity-slider-card in the UI got jacked up when this automation turned the light on without also setting the brightness. So, the action that gets called is this script below, stolen from elsewhere in this forum.
#########
# python_scripts/set_entity_state.py
# Set the state or other attributes for the entity specified in an Automation Action
#########
inputEntity = data.get('entity_id')
if inputEntity is None:
logger.warning("===== entity_id is required if you want to set something.")
else:
inputStateObject = hass.states.get(inputEntity)
inputState = inputStateObject.state
inputAttributesObject = inputStateObject.attributes.copy()
for item in data:
newAttribute = data.get(item)
logger.debug("===== item = {0}; value = {1}".format(item,newAttribute))
if item == 'entity_id':
continue # already handled
elif item == 'state':
inputState = newAttribute
else:
inputAttributesObject[item] = newAttribute
hass.states.set(inputEntity, inputState, inputAttributesObject)
Then there are these. They completely duplicate the scenes programmed in Insteon. When I push a button on keypad, the Insteon scene fires and does all this so that the four scene buttons on the keypad never show that the fan is in more than one state. But in HA I’m interacting directly with the fan, so I have a series of automations, one for each fan speed, to set the keypad to match the state we just set the fan to.
#########
## These automations update the state of the keypad to match what the HA just did to fan
## Four buttons on keypad map to fan speeds: off, low, medium, high.
## Scenes in insteon do this same action when button is pressed on keypad, but HA interacts with fan directly
## so these automations replicate those scenes to make button lights match fan speed.
#########
- alias: Keypad to match left fan speed off
trigger:
# when the button in HA is pressed to set fan speed to off
platform: template
value_template: "{{ is_state_attr('fan.fanlinc_X_fan', 'speed', 'off') }}"
action:
# set the state of each of the four keypad scene button lights so the off button is only one lit
- service: switch.turn_off
entity_id: switch.keypad_with_dimmer_X_button_a
- service: switch.turn_off
entity_id: switch.keypad_with_dimmer_X_button_b
- service: switch.turn_off
entity_id: switch.keypad_with_dimmer_X_button_c
- service: switch.turn_on
entity_id: switch.keypad_with_dimmer_X_button_d
# and then this repeats four times, once for each button with the on/off states changing as appropriate
There is undoubtedly a cleaner way to do all this. I went went brute force. I suspect through templates and scripts there is probably a way to combine multiple of the above automations together. But at this point it works and I have other things to work on. Also, above is for one fan. Everything repeats for each fan.