# frozen_string_literal: true
module ButtonAction
ON = 'on'
OFF = 'off'
BRIGHTNESS_UP = 'brightness_move_up'
BRIGHTNESS_DOWN = 'brightness_move_down'
BRIGHTNESS_STOP = 'brightness_stop'
end
def blink(dimmer)
power = dimmer.points(Semantics::Power).first
power.off
ensure
after(350.ms) do
power.on
end
end
def adjust(dimmer, delta)
return timers[dimmer]&.cancel if delta.zero?
dimmer.points(Semantics::Power).first&.ensure.on
after(0.ms, id: dimmer) do |timer|
next blink(dimmer) if (dimmer == '100 %' && delta.positive?) || (dimmer <= '1 %' && delta.negative?)
dimmer << (dimmer.to_i + delta).clamp(1, 100)
timer.reschedule 100.ms
end
end
rule 'Rodret Dimmer Handler' do
updated rodretDimmers.members
triggered do |action|
light = action.equipment
next unless light
dimmer = light.points(Semantics::Light).first
power = light.points(Semantics::Power).first
case action.state
when ButtonAction::ON then power.ensure.on
when ButtonAction::OFF then power.ensure.off
when ButtonAction::BRIGHTNESS_UP then adjust(dimmer, 5)
when ButtonAction::BRIGHTNESS_DOWN then adjust(dimmer, -5)
when ButtonAction::BRIGHTNESS_STOP then adjust(dimmer, 0)
end
end
end
light = action.equipment
next unless light
dimmer = light.points(Semantics::Light).first
power = light.points(Semantics::Power).first
This is because dimmer and/or power is nil.
The way the script is designed, your dimmer needs to be a semantic Point (which I believe it is), but the script is looking for a sibling point with Power property, and because it didnāt find it, power is nil.
So you might need to adjust the script or your semantic model.
I think I knowā¦ itās yet another v4 to v5 breaking change.
This is your code (v4)
after(0.ms, id: dimmer) do |timer|
next blink(dimmer) if (dimmer == '100 %' && delta.positive?) || (dimmer <= '1 %' && delta.negative?)
dimmer << (dimmer.to_i + delta).clamp(1, 100)
timer.reschedule 100.ms
end
This is the conversion to v5:
after(0.ms, id: dimmer) do |timer|
next blink(dimmer) if (dimmer.state.to_i == 100 && delta.positive?) || (dimmer.state.to_i <= 1 && delta.negative?)
dimmer << (dimmer.state.to_i + delta).clamp(1, 100)
timer.reschedule 100.ms
end
Explanation: In v4, you can treat an item as if itās the state, so you can write if DimmerItem1 == "100 %"
In v5, you canāt do that anymore because it caused ambiguities, so instead, youād have to specify that youāre referring to the state i.e. if DimmerItem.state.to_i == 100
Another change was the comparison was made stricter. You canāt just compare it with string, so youād either have to convert the state to an integer and compare it against a number or, compare the state to a PercentType, e.g. if DimmerItem1.state == PercentType.new(100). I chose to use .to_i usually unless Iām dealing with QuantityType, in which case Iād compare the state against a QuantityType.
Youāve mixed my original example with the code from @jabra_the_hut. His button is different and sends different codes to the IKEA styrbar / zigbee2mqtt. On his itās a numeric code instead of string names.
Okay, I can see the keypresses now in the log.
Unfortunately I think there is still something messed up between the lookup and the case-statement, since now it does nothing but log the keys:
rule 'Rodret Dimmer Handler' do
updated rodretDimmers.members
run do |event|
light = event.item.equipment
next unless light
dimmer = light.points(Semantics::Light).first
power = light.points(Semantics::Power).first
action_name = BUTTON_ACTIONS[event.state]
logger.info("Rodret Dimmer Handler - button pressed: #{action_name} (#{event.state})")
case action_name
when :on then power.ensure.on
when :off then power.ensure.off
when :brightness_up then adjust(dimmer, 5)
when :brightness_down then adjust(dimmer, -5)
when :brightness_stop then adjust(dimmer, 0)
end
end
end
Btw the log output looks like it āskipsā the āaction_nameā variable and just outputs āevent.stateā:
action_name = BUTTON_ACTIONS[event.state]
logger.info("Rodret Dimmer Handler - button pressed: #{action_name} (#{event.state})")
case action_name
when :on then power.ensure.on
when :off then power.ensure.off
when :brightness_up then adjust(dimmer, 5)
when :brightness_down then adjust(dimmer, -5)
when :brightness_stop then adjust(dimmer, 0)
end
to this:
action_name = event.state
logger.info("Rodret Dimmer Handler - button pressed: #{action_name}")
case action_name
when 'on' then power.ensure.on
when 'off' then power.ensure.off
when 'brightness_move_up' then adjust(dimmer, 5)
when 'brightness_move_down' then adjust(dimmer, -5)
when 'brightness_stop' then adjust(dimmer, 0)
end
The problem with the first code is: event.state is a StringType, not a plain string, so the lookup into BUTTON_ACTION didnāt result in a match. In this case BUTTON_ACTION is not really that useful so just remove it and use the direct value.
This time, comparison within case/when between StringType and String would work, and BUTTON_ACTION is not needed
The original idea was to use constants for these reasons:
Shorter
If thereās a typo, it would result in an error that I could detect earlier on
The original code was this (and itās still my preference)
module ButtonAction
ON = 'on'
OFF = 'off'
BRIGHTNESS_UP = 'brightness_move_up'
BRIGHTNESS_DOWN = 'brightness_move_down'
BRIGHTNESS_STOP = 'brightness_stop'
LEFT = 'arrow_left_click'
LEFT_HOLD = 'arrow_left_hold'
LEFT_RELEASE = 'arrow_left_release'
RIGHT = 'arrow_right_click'
RIGHT_HOLD = 'arrow_right_hold'
RIGHT_RELEASE = 'arrow_right_release'
end
rule 'Ikea Dimmer Handler' do
updated gDimmers.members
triggered do |action|
...
case action.state
when ButtonAction::ON then # do something
when ButtonAction::OFF then # do something
when ButtonAction::BRIGHTNESS_UP then # do something
when ButtonAction::BRIGHTNESS_DOWN then # do something
when ButtonAction::BRIGHTNESS_STOP then # do something
...
end
end
end