Skip to content

Commit

Permalink
Sitemaps: Add Button widget support
Browse files Browse the repository at this point in the history
Signed-off-by: Jimmy Tanagra <[email protected]>
  • Loading branch information
jimtng committed May 21, 2024
1 parent 6f529aa commit 8770042
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 70 deletions.
285 changes: 215 additions & 70 deletions lib/openhab/dsl/sitemaps/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -678,89 +678,81 @@ def build
end
end

# Builds a `Buttongrid` element
# @since openHAB 4.1
# @see https://www.openhab.org/docs/ui/sitemaps.html#element-type-buttongrid
# @see org.openhab.core.model.sitemap.sitemap.Buttongrid
class ButtongridBuilder < WidgetBuilder
# @return [Array<Array<int, int, Command, String, String>>]
# An array of buttons to display
attr_reader :buttons
# Builds a `Button` element
#
# This element can only exist within a ButtonGrid element.
#
# @since openHAB 4.2
# @see https://www.openhab.org/docs/ui/sitemaps.html#element-type-button
# @see org.openhab.core.model.sitemap.sitemap.Button
class ButtonBuilder < WidgetBuilder
# The row in which the button is placed
# @return [Integer]
attr_accessor :row

# The column in which the button is placed
# @return [Integer]
attr_accessor :column

# The command to send when the button is pressed
# @return [String, Command]
attr_accessor :click

# The command to send when the button is released
# @return [String, Command, nil]
attr_accessor :release

# Whether the button is stateless
# @return [true, false, nil]
attr_writer :stateless

# (see WidgetBuilder#initialize)
# @!method initialize(item: nil, label: nil, icon: nil, static_icon: nil, buttons: nil, label_color: nil, value_color: nil, icon_color: nil, visibility: nil)
# @param [Array<Array<int, int, Command, String, String>>] buttons An array of buttons to display.
# Each element is an array with the following elements:
# - row: 1-12
# - column: 1-12
# - command: The command to send when the button is pressed
# - label: The label to display on the button
# - icon: The icon to display on the button (optional)
#
# @example
# # This creates a buttongrid to emulate a TV remote control
# sitemaps.build do
# sitemap "remote", label: "TV Remote Control" do
# buttongrid item: LivingRoom_TV_RCButton, buttons: [
# [1, 1, "BACK", "Back", "f7:return"],
# [1, 2, "HOME", "Menu", "material:apps"],
# [1, 3, "YELLOW", "Search", "f7:search"],
# [2, 2, "UP", "Up", "f7:arrowtriangle_up"],
# [4, 2, "DOWN", "Down", "f7:arrowtriangle_down"],
# [3, 1, "LEFT", "Left", "f7:arrowtriangle_left"],
# [3, 3, "RIGHT", "Right", "f7:arrowtriangle_right"],
# [3, 2, "ENTER", "Enter", "material:adjust"]
# ]
# end
# end
# @!method initialize(item: nil, label: nil, icon: nil, static_icon: nil, row:, column:, click:, release: nil, stateless: nil, label_color: nil, value_color: nil, icon_color: nil, visibility: nil)
# @param [Integer] row
# The row in which the button is placed (see {ButtonBuilder#row})
# @param [Integer] column
# The column in which the button is placed (see {ButtonBuilder#column})
# @param [String, Command] click
# The command to send when the button is pressed (see {ButtonBuilder#click})
# @param [String, Command, nil] release
# The command to send when the button is released (see {ButtonBuilder#release})
# @param [true, false, nil] stateless
# Whether the button is stateless (see {ButtonBuilder#stateless=})
#
# @see https://www.openhab.org/docs/ui/sitemaps.html#element-type-buttongrid
# @!visibility private
def initialize(type, builder_proxy, buttons: [], **kwargs, &block)
@buttons = buttons
super(type, builder_proxy, **kwargs, &block)
buttons.each { |button| validate_button(button) }
def initialize(builder_proxy,
row:,
column:,
click:,
release: nil,
stateless: nil,
**kwargs,
&block)

super(:button, builder_proxy, **kwargs, &block)

@row = row
@column = column
@click = click
@release = release
@stateless = stateless
end

#
# Adds a button to the buttongrid
#
# @param [Array<int, int, Command, String, String>] button the button to add
# @return [Array<Array<int, int, Command, String, String>>] the current buttons
#
def button(button)
validate_button(button)
@buttons << button
# (see #stateless=)
def stateless?
@stateless
end

# @!visibility private
def build
widget = super
buttons.each do |button|
button_object = if SitemapBuilder.factory.respond_to?(:create_button_definition)
SitemapBuilder.factory.create_button_definition
else
# @deprecated OH 4.1 in OH 4.2 this clause is not needed
SitemapBuilder.factory.create_button
end
button_object.row = button[0]
button_object.column = button[1]
button_object.cmd = button[2]
button_object.label = button[3]
button_object.icon = button[4] if button[4]
widget.buttons.add(button_object)
end

widget.row = row
widget.column = column
widget.cmd = click.to_s
widget.release_cmd = release.to_s unless release.nil?
widget.stateless = stateless? unless @stateless.nil?
widget
end

private

def validate_button(button)
return if (4..5).cover?(button.size)

raise ArgumentError, "Invalid button: '#{button.inspect}'. It must be an array with (4..5) elements"
end
end

# Parent class for builders of widgets that can contain other widgets.
Expand Down Expand Up @@ -1148,6 +1140,159 @@ def build
class FrameBuilder < LinkableWidgetBuilder
end

# Builds a `Buttongrid` element
# @since openHAB 4.1
# @see https://www.openhab.org/docs/ui/sitemaps.html#element-type-buttongrid
# @see org.openhab.core.model.sitemap.sitemap.Buttongrid
class ButtongridBuilder < LinkableWidgetBuilder
# @return [Array<Array<int, int, Command, String, String>>]
# An array of buttons to display
attr_reader :buttons

# (see WidgetBuilder#initialize)
# @!method initialize(item: nil, label: nil, icon: nil, static_icon: nil, buttons: nil, label_color: nil, value_color: nil, icon_color: nil, visibility: nil)
# @param [Array<Array<int, int, Command, String, String>>] buttons An array of buttons to display.
# Each element is an array with the following elements:
# - row: 1-12
# - column: 1-12
# - command: The command to send when the button is pressed
# - label: The label to display on the button
# - icon: The icon to display on the button (optional)
#
# @example
# # This creates a buttongrid to emulate a TV remote control
# sitemaps.build do
# sitemap "remote", label: "TV Remote Control" do
# buttongrid item: LivingRoom_TV_RCButton, buttons: [
# [1, 1, "BACK", "Back", "f7:return"],
# [1, 2, "HOME", "Menu", "material:apps"],
# [1, 3, "YELLOW", "Search", "f7:search"],
# [2, 2, "UP", "Up", "f7:arrowtriangle_up"],
# [4, 2, "DOWN", "Down", "f7:arrowtriangle_down"],
# [3, 1, "LEFT", "Left", "f7:arrowtriangle_left"],
# [3, 3, "RIGHT", "Right", "f7:arrowtriangle_right"],
# [3, 2, "ENTER", "Enter", "material:adjust"]
# ]
# end
# end
#
# @see https://www.openhab.org/docs/ui/sitemaps.html#element-type-buttongrid
# @!visibility private
def initialize(type, builder_proxy, buttons: [], **kwargs, &block)
@buttons = buttons
buttons.each { |button| validate_button(button) }
super(type, builder_proxy, **kwargs, &block)
end

#
# Adds a button to the buttongrid.
#
# openHAB 4.2 introduced the ability to have Button elements inside a Buttongrid.
# Each Button element is an independent widget linked to its own item.
# This means that a buttongrid can contain multiple buttons that each controls different items.
#
# @example Adding buttons to a buttongrid
# sitemaps.build do
# sitemap "remote" do
# buttongrid item: RCButton do
# button [1, 1, "BACK", "Back", "f7:return"]
# button [1, 2, "HOME", "Menu", "material:apps"]
# button [1, 3, "YELLOW", "Search", "f7:search"]
# button [2, 2, "UP", "Up", "f7:arrowtriangle_up"]
# button [4, 2, "DOWN", "Down", "f7:arrowtriangle_down"]
# button [3, 1, "LEFT", "Left", "f7:arrowtriangle_left"]
# button [3, 3, "RIGHT", "Right", "f7:arrowtriangle_right"]
# button [3, 2, "ENTER", "Enter", "material:adjust"]
# end
# end
# end
#
# @example Adding Button widgets to a buttongrid
# sitemaps.build do
# sitemap "remote" do
# buttongrid do
# button item: RCButton, row: 1, column: 1, click: "BACK", icon: "f7:return"
# button item: RCButton, row: 1, column: 2, click: "HOME", icon: "material:apps"
# button item: RCButton, row: 1, column: 3, click: "YELLOW", icon: "f7:search"
# button item: RCButton, row: 2, column: 2, click: "UP", icon: "f7:arrowtriangle_up"
# button item: RCButton, row: 4, column: 2, click: "DOWN", icon: "f7:arrowtriangle_down"
# button item: RCButton, row: 3, column: 1, click: "LEFT", icon: "f7:arrowtriangle_left"
# button item: RCButton, row: 3, column: 3, click: "RIGHT", icon: "f7:arrowtriangle_right"
# button item: RCButton, row: 3, column: 2, click: "ENTER", icon: "material:adjust"
# button item: TVPower, row: 4, column: 3, click: ON, icon: "switch-on", visibility: "TVPower!=ON"
# button item: TVPower, row: 4, column: 3, click: OFF, icon: "switch-off", visibility: "TVPower==ON"
# end
# end
# end
#
#
# @overload button(button)
# Adds a button to the buttongrid's main item
#
# @param [Array<int, int, Command, String, String>] button
# The button to add, which is an array with the following elements:
# - row: 1-12
# - column: 1-12
# - command: The command to send when the button is pressed
# - label: The label to display on the button
# - icon: The icon to display on the button (optional)
# @return [Array<Array<int, int, Command, String, String>>] the current buttons
#
# @overload button(item: nil, label: nil, icon: nil, static_icon: nil, row:, column:, click:, release: nil, stateless: nil, label_color: nil, value_color: nil, icon_color: nil, visibility: nil)
# Adds a Button widget inside the buttongrid
# @param (see OpenHAB::DSL::Sitemaps::ButtonBuilder#initialize)
# @return [ButtonBuilder]
#
# @since openHAB 4.2
#
def button(button = nil, **kwargs, &block)
if button.nil?
missing_args = (%i[row column click] - kwargs.keys).compact
unless missing_args.empty?
args = kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
missing_args = missing_args.map(&:to_s).join(", ")
raise ArgumentError, "button(#{args}) missing required parameters: #{missing_args}"
end

widget = ButtonBuilder.new(@builder_proxy, **kwargs, &block)
children << widget
return widget
end

validate_button(button)
@buttons << button
end

# @!visibility private
def build
widget = super
buttons.each do |button|
button_object = if SitemapBuilder.factory.respond_to?(:create_button_definition)
SitemapBuilder.factory.create_button_definition
else
# @deprecated OH 4.1 in OH 4.2 this clause is not needed
SitemapBuilder.factory.create_button
end
button_object.row = button[0]
button_object.column = button[1]
button_object.cmd = button[2]
button_object.label = button[3]
button_object.icon = button[4] if button[4]
widget.buttons.add(button_object)
end

widget
end

private

def validate_button(button)
return if button.is_a?(Array) && (4..5).cover?(button.size)

raise ArgumentError, "Invalid button: '#{button.inspect}'. It must be an array with (4..5) elements"
end
end

# Builds a `Sitemap`
# @see https://www.openhab.org/docs/ui/sitemaps.html
# @see org.openhab.core.model.sitemap.sitemap.Sitemap
Expand Down
37 changes: 37 additions & 0 deletions spec/openhab/dsl/sitemaps/builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,43 @@
end
end.to raise_error(ArgumentError)
end

# @deprecated OH 4.1 guard is only needed for < OH 4.2
describe "with button sub-widgets", if: OpenHAB::Core.version >= OpenHAB::Core::V4_2 do
it "works" do
s = sitemaps.build do
sitemap "default" do
buttongrid do
button row: 1, column: 1, click: "BACK", label: "Back", icon: "f7:return"
button row: 1, column: 2, click: "HOME", label: "Menu", icon: "material:apps"
end
end
end

bg = s.children.first
buttons = bg.children
expect(buttons.size).to eq 2
expect(buttons[0].row).to eq 1
expect(buttons[1].cmd).to eq "HOME"
end

{ row: 1, column: 1, click: "CMD" }.tap do |arguments|
arguments.each_key do |arg|
it "raises an error when '#{arg}' argument is missing" do
expect do
sitemaps.build do
sitemap "default" do
buttongrid do
arguments_with_missing_key = arguments.reject { |k, _| k == arg }
button(**arguments_with_missing_key)
end
end
end
end.to raise_error(ArgumentError)
end
end
end
end
end

it "can add a default" do
Expand Down

0 comments on commit 8770042

Please sign in to comment.