openhab-scripting 4.46.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/openhab/core/actions/audio.rb +47 -0
- data/lib/openhab/core/actions/ephemeris.rb +39 -0
- data/lib/openhab/core/actions/exec.rb +51 -0
- data/lib/openhab/core/actions/http.rb +80 -0
- data/lib/openhab/core/actions/ping.rb +30 -0
- data/lib/openhab/core/actions/transformation.rb +32 -0
- data/lib/openhab/core/actions/voice.rb +36 -0
- data/lib/openhab/core/actions.rb +82 -0
- data/lib/openhab/core/dependency_tracking.rb +34 -0
- data/lib/openhab/core/dto/item_channel_link.rb +33 -0
- data/lib/openhab/core/dto/thing.rb +27 -0
- data/lib/openhab/core/dto.rb +11 -0
- data/lib/openhab/core/entity_lookup.rb +152 -70
- data/lib/openhab/core/events/abstract_event.rb +18 -0
- data/lib/openhab/core/events/abstract_item_registry_event.rb +36 -0
- data/lib/openhab/core/events/abstract_thing_registry_event.rb +40 -0
- data/lib/openhab/core/events/item_command_event.rb +78 -0
- data/lib/openhab/core/events/item_event.rb +22 -0
- data/lib/openhab/core/events/item_state_changed_event.rb +75 -0
- data/lib/openhab/core/events/item_state_event.rb +79 -0
- data/lib/openhab/core/events/thing_status_info_event.rb +55 -0
- data/lib/openhab/core/events.rb +10 -0
- data/lib/openhab/core/items/accepted_data_types.rb +29 -0
- data/lib/openhab/core/items/color_item.rb +52 -0
- data/lib/openhab/core/items/contact_item.rb +52 -0
- data/lib/openhab/core/items/date_time_item.rb +59 -0
- data/lib/openhab/core/items/dimmer_item.rb +148 -0
- data/lib/openhab/core/items/generic_item.rb +292 -0
- data/lib/openhab/core/items/group_item.rb +176 -0
- data/lib/openhab/{dsl → core}/items/image_item.rb +35 -29
- data/lib/openhab/core/items/item.rb +273 -0
- data/lib/openhab/core/items/location_item.rb +34 -0
- data/lib/openhab/core/items/metadata/hash.rb +433 -0
- data/lib/openhab/core/items/metadata/namespace_hash.rb +475 -0
- data/lib/openhab/core/items/metadata/provider.rb +48 -0
- data/lib/openhab/core/items/metadata.rb +11 -0
- data/lib/openhab/core/items/number_item.rb +62 -0
- data/lib/openhab/core/items/numeric_item.rb +22 -0
- data/lib/openhab/core/items/persistence.rb +416 -0
- data/lib/openhab/core/items/player_item.rb +66 -0
- data/lib/openhab/core/items/provider.rb +44 -0
- data/lib/openhab/core/items/proxy.rb +136 -0
- data/lib/openhab/core/items/registry.rb +86 -0
- data/lib/openhab/core/items/rollershutter_item.rb +68 -0
- data/lib/openhab/core/items/semantics/enumerable.rb +177 -0
- data/lib/openhab/core/items/semantics.rb +473 -0
- data/lib/openhab/core/items/state_storage.rb +53 -0
- data/lib/openhab/core/items/string_item.rb +28 -0
- data/lib/openhab/core/items/switch_item.rb +78 -0
- data/lib/openhab/core/items.rb +108 -0
- data/lib/openhab/{dsl → core}/lazy_array.rb +9 -3
- data/lib/openhab/core/profile_factory.rb +132 -0
- data/lib/openhab/core/provider.rb +230 -0
- data/lib/openhab/core/proxy.rb +130 -0
- data/lib/openhab/core/registry.rb +40 -0
- data/lib/openhab/core/rules/module.rb +26 -0
- data/lib/openhab/core/rules/provider.rb +25 -0
- data/lib/openhab/core/rules/registry.rb +76 -0
- data/lib/openhab/core/rules/rule.rb +150 -0
- data/lib/openhab/core/rules.rb +25 -0
- data/lib/openhab/core/script_handling.rb +78 -20
- data/lib/openhab/core/things/channel.rb +48 -0
- data/lib/openhab/core/things/channel_uid.rb +51 -0
- data/lib/openhab/core/things/item_channel_link.rb +33 -0
- data/lib/openhab/core/things/links/provider.rb +78 -0
- data/lib/openhab/core/things/profile_callback.rb +52 -0
- data/lib/openhab/core/things/provider.rb +29 -0
- data/lib/openhab/core/things/proxy.rb +87 -0
- data/lib/openhab/core/things/registry.rb +73 -0
- data/lib/openhab/core/things/thing.rb +194 -0
- data/lib/openhab/core/things.rb +22 -0
- data/lib/openhab/core/timer.rb +148 -0
- data/lib/openhab/{dsl → core}/types/comparable_type.rb +5 -3
- data/lib/openhab/{dsl → core}/types/date_time_type.rb +55 -127
- data/lib/openhab/{dsl → core}/types/decimal_type.rb +50 -48
- data/lib/openhab/{dsl → core}/types/hsb_type.rb +35 -83
- data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
- data/lib/openhab/core/types/next_previous_type.rb +34 -0
- data/lib/openhab/{dsl → core}/types/numeric_type.rb +20 -7
- data/lib/openhab/core/types/on_off_type.rb +46 -0
- data/lib/openhab/core/types/open_closed_type.rb +41 -0
- data/lib/openhab/{dsl → core}/types/percent_type.rb +19 -20
- data/lib/openhab/core/types/play_pause_type.rb +38 -0
- data/lib/openhab/core/types/point_type.rb +117 -0
- data/lib/openhab/core/types/quantity_type.rb +325 -0
- data/lib/openhab/core/types/raw_type.rb +26 -0
- data/lib/openhab/core/types/refresh_type.rb +27 -0
- data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
- data/lib/openhab/core/types/stop_move_type.rb +34 -0
- data/lib/openhab/{dsl → core}/types/string_type.rb +17 -28
- data/lib/openhab/{dsl → core}/types/type.rb +42 -40
- data/lib/openhab/core/types/un_def_type.rb +38 -0
- data/lib/openhab/core/types/up_down_type.rb +50 -0
- data/lib/openhab/core/types.rb +82 -0
- data/lib/openhab/{dsl → core}/uid.rb +4 -23
- data/lib/openhab/core/value_cache.rb +188 -0
- data/lib/openhab/core.rb +98 -0
- data/lib/openhab/core_ext/between.rb +32 -0
- data/lib/openhab/core_ext/ephemeris.rb +53 -0
- data/lib/openhab/core_ext/java/class.rb +34 -0
- data/lib/openhab/core_ext/java/duration.rb +142 -0
- data/lib/openhab/core_ext/java/list.rb +436 -0
- data/lib/openhab/core_ext/java/local_date.rb +104 -0
- data/lib/openhab/core_ext/java/local_time.rb +118 -0
- data/lib/openhab/core_ext/java/map.rb +66 -0
- data/lib/openhab/core_ext/java/month.rb +71 -0
- data/lib/openhab/core_ext/java/month_day.rb +119 -0
- data/lib/openhab/core_ext/java/period.rb +103 -0
- data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
- data/lib/openhab/core_ext/java/time.rb +62 -0
- data/lib/openhab/core_ext/java/unit.rb +15 -0
- data/lib/openhab/core_ext/java/zoned_date_time.rb +213 -0
- data/lib/openhab/core_ext/ruby/array.rb +21 -0
- data/lib/openhab/core_ext/ruby/date.rb +96 -0
- data/lib/openhab/core_ext/ruby/date_time.rb +55 -0
- data/lib/openhab/core_ext/ruby/module.rb +15 -0
- data/lib/openhab/core_ext/ruby/numeric.rb +195 -0
- data/lib/openhab/core_ext/ruby/range.rb +70 -0
- data/lib/openhab/core_ext/ruby/symbol.rb +7 -0
- data/lib/openhab/core_ext/ruby/time.rb +108 -0
- data/lib/openhab/core_ext.rb +18 -0
- data/lib/openhab/dsl/debouncer.rb +259 -0
- data/lib/openhab/dsl/events/watch_event.rb +18 -0
- data/lib/openhab/dsl/events.rb +9 -0
- data/lib/openhab/dsl/gems.rb +1 -1
- data/lib/openhab/dsl/items/builder.rb +578 -0
- data/lib/openhab/dsl/items/ensure.rb +73 -82
- data/lib/openhab/dsl/items/timed_command.rb +214 -159
- data/lib/openhab/dsl/rules/automation_rule.rb +126 -115
- data/lib/openhab/dsl/rules/builder.rb +1935 -0
- data/lib/openhab/dsl/rules/guard.rb +51 -114
- data/lib/openhab/dsl/rules/name_inference.rb +66 -25
- data/lib/openhab/dsl/rules/property.rb +48 -75
- data/lib/openhab/dsl/rules/rule_triggers.rb +22 -27
- data/lib/openhab/dsl/rules/terse.rb +58 -14
- data/lib/openhab/dsl/rules/triggers/changed.rb +48 -94
- data/lib/openhab/dsl/rules/triggers/channel.rb +9 -40
- data/lib/openhab/dsl/rules/triggers/command.rb +14 -63
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +34 -69
- data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +6 -14
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +48 -82
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +30 -47
- data/lib/openhab/dsl/rules/triggers/trigger.rb +7 -28
- data/lib/openhab/dsl/rules/triggers/updated.rb +21 -45
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +257 -102
- data/lib/openhab/dsl/rules/triggers.rb +12 -0
- data/lib/openhab/dsl/rules.rb +8 -0
- data/lib/openhab/dsl/things/builder.rb +299 -0
- data/lib/openhab/{core → dsl}/thread_local.rb +27 -17
- data/lib/openhab/dsl/timer_manager.rb +204 -0
- data/lib/openhab/dsl/version.rb +9 -0
- data/lib/openhab/dsl.rb +979 -0
- data/lib/openhab/log.rb +355 -0
- data/lib/openhab/osgi.rb +68 -0
- data/lib/openhab/rspec/configuration.rb +56 -0
- data/lib/openhab/rspec/example_group.rb +132 -0
- data/lib/openhab/rspec/helpers.rb +458 -0
- data/lib/openhab/rspec/hooks.rb +113 -0
- data/lib/openhab/rspec/jruby.rb +46 -0
- data/lib/openhab/rspec/karaf.rb +851 -0
- data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
- data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
- data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
- data/lib/openhab/rspec/mocks/instance_method_stasher.rb +22 -0
- data/lib/openhab/rspec/mocks/persistence_service.rb +155 -0
- data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
- data/lib/openhab/rspec/mocks/space.rb +23 -0
- data/lib/openhab/rspec/mocks/synchronous_executor.rb +63 -0
- data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
- data/lib/openhab/rspec/mocks/timer.rb +134 -0
- data/lib/openhab/rspec/openhab/core/actions.rb +38 -0
- data/lib/openhab/rspec/openhab/core/items/proxy.rb +15 -0
- data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
- data/lib/openhab/rspec/shell.rb +31 -0
- data/lib/openhab/rspec/suspend_rules.rb +50 -0
- data/lib/openhab/rspec.rb +26 -0
- data/lib/openhab/yard/base_helper.rb +19 -0
- data/lib/openhab/yard/cli/stats.rb +23 -0
- data/lib/openhab/yard/code_objects/group_object.rb +23 -0
- data/lib/openhab/yard/code_objects/java/base.rb +31 -0
- data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
- data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
- data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
- data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
- data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
- data/lib/openhab/yard/coderay.rb +17 -0
- data/lib/openhab/yard/handlers/jruby/base.rb +58 -0
- data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
- data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
- data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +30 -0
- data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
- data/lib/openhab/yard/html_helper.rb +78 -0
- data/lib/openhab/yard/markdown_helper.rb +148 -0
- data/lib/openhab/yard/tags/constant_directive.rb +20 -0
- data/lib/openhab/yard/tags/group_directive.rb +24 -0
- data/lib/openhab/yard/tags/library.rb +3 -0
- data/lib/openhab/yard.rb +38 -0
- metadata +475 -106
- data/lib/openhab/core/item_proxy.rb +0 -29
- data/lib/openhab/core/load_path.rb +0 -19
- data/lib/openhab/core/openhab_setup.rb +0 -29
- data/lib/openhab/core/osgi.rb +0 -58
- data/lib/openhab/core/services.rb +0 -24
- data/lib/openhab/dsl/actions.rb +0 -114
- data/lib/openhab/dsl/between.rb +0 -25
- data/lib/openhab/dsl/channel.rb +0 -43
- data/lib/openhab/dsl/dsl.rb +0 -59
- data/lib/openhab/dsl/group.rb +0 -54
- data/lib/openhab/dsl/imports.rb +0 -21
- data/lib/openhab/dsl/items/color_item.rb +0 -76
- data/lib/openhab/dsl/items/comparable_item.rb +0 -62
- data/lib/openhab/dsl/items/contact_item.rb +0 -41
- data/lib/openhab/dsl/items/date_time_item.rb +0 -65
- data/lib/openhab/dsl/items/dimmer_item.rb +0 -65
- data/lib/openhab/dsl/items/generic_item.rb +0 -229
- data/lib/openhab/dsl/items/group_item.rb +0 -127
- data/lib/openhab/dsl/items/item_equality.rb +0 -59
- data/lib/openhab/dsl/items/item_registry.rb +0 -54
- data/lib/openhab/dsl/items/items.rb +0 -109
- data/lib/openhab/dsl/items/location_item.rb +0 -59
- data/lib/openhab/dsl/items/metadata.rb +0 -326
- data/lib/openhab/dsl/items/number_item.rb +0 -17
- data/lib/openhab/dsl/items/numeric_item.rb +0 -87
- data/lib/openhab/dsl/items/persistence.rb +0 -307
- data/lib/openhab/dsl/items/player_item.rb +0 -58
- data/lib/openhab/dsl/items/rollershutter_item.rb +0 -51
- data/lib/openhab/dsl/items/semantics/enumerable.rb +0 -91
- data/lib/openhab/dsl/items/semantics.rb +0 -227
- data/lib/openhab/dsl/items/string_item.rb +0 -51
- data/lib/openhab/dsl/items/switch_item.rb +0 -70
- data/lib/openhab/dsl/monkey_patch/actions/actions.rb +0 -4
- data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +0 -39
- data/lib/openhab/dsl/monkey_patch/events/events.rb +0 -7
- data/lib/openhab/dsl/monkey_patch/events/item_command.rb +0 -85
- data/lib/openhab/dsl/monkey_patch/events/item_event.rb +0 -28
- data/lib/openhab/dsl/monkey_patch/events/item_state.rb +0 -61
- data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +0 -60
- data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +0 -33
- data/lib/openhab/dsl/monkey_patch/java/java.rb +0 -4
- data/lib/openhab/dsl/monkey_patch/java/local_time.rb +0 -44
- data/lib/openhab/dsl/monkey_patch/java/time_extensions.rb +0 -50
- data/lib/openhab/dsl/monkey_patch/java/zoned_date_time.rb +0 -45
- data/lib/openhab/dsl/monkey_patch/ruby/number.rb +0 -104
- data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +0 -6
- data/lib/openhab/dsl/monkey_patch/ruby/string.rb +0 -47
- data/lib/openhab/dsl/monkey_patch/ruby/time.rb +0 -61
- data/lib/openhab/dsl/openhab.rb +0 -30
- data/lib/openhab/dsl/persistence.rb +0 -27
- data/lib/openhab/dsl/rules/item_event.rb +0 -19
- data/lib/openhab/dsl/rules/rule.rb +0 -160
- data/lib/openhab/dsl/rules/rule_config.rb +0 -147
- data/lib/openhab/dsl/rules/triggers/generic.rb +0 -31
- data/lib/openhab/dsl/rules/triggers/triggers.rb +0 -11
- data/lib/openhab/dsl/rules/triggers/watch/watch.rb +0 -81
- data/lib/openhab/dsl/states.rb +0 -89
- data/lib/openhab/dsl/things.rb +0 -147
- data/lib/openhab/dsl/time/month_day.rb +0 -180
- data/lib/openhab/dsl/time/time_of_day.rb +0 -235
- data/lib/openhab/dsl/timers/manager.rb +0 -119
- data/lib/openhab/dsl/timers/reentrant_timer.rb +0 -38
- data/lib/openhab/dsl/timers/timer.rb +0 -132
- data/lib/openhab/dsl/timers.rb +0 -77
- data/lib/openhab/dsl/types/increase_decrease_type.rb +0 -23
- data/lib/openhab/dsl/types/next_previous_type.rb +0 -23
- data/lib/openhab/dsl/types/on_off_type.rb +0 -28
- data/lib/openhab/dsl/types/open_closed_type.rb +0 -29
- data/lib/openhab/dsl/types/play_pause_type.rb +0 -27
- data/lib/openhab/dsl/types/point_type.rb +0 -180
- data/lib/openhab/dsl/types/quantity_type.rb +0 -265
- data/lib/openhab/dsl/types/refresh_type.rb +0 -18
- data/lib/openhab/dsl/types/rewind_fastforward_type.rb +0 -33
- data/lib/openhab/dsl/types/stop_move_type.rb +0 -23
- data/lib/openhab/dsl/types/types.rb +0 -83
- data/lib/openhab/dsl/types/un_def_type.rb +0 -22
- data/lib/openhab/dsl/types/up_down_type.rb +0 -32
- data/lib/openhab/dsl/units.rb +0 -45
- data/lib/openhab/log/configuration.rb +0 -21
- data/lib/openhab/log/logger.rb +0 -282
- data/lib/openhab/version.rb +0 -9
- data/lib/openhab.rb +0 -36
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenHAB
|
|
4
|
+
module DSL
|
|
5
|
+
#
|
|
6
|
+
# Contains extensions to simplify working with {Item Items}.
|
|
7
|
+
#
|
|
8
|
+
module Items
|
|
9
|
+
# An item builder allows you to dynamically create openHAB items at runtime.
|
|
10
|
+
# This can be useful either to create items as soon as the script loads,
|
|
11
|
+
# or even later based on a rule executing.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# items.build do
|
|
15
|
+
# switch_item MySwitch, "My Switch"
|
|
16
|
+
# switch_item NotAutoupdating, autoupdate: false, channel: "mqtt:topic:1#light"
|
|
17
|
+
# group_item MyGroup do
|
|
18
|
+
# contact_item ItemInGroup, channel: "binding:thing#channel"
|
|
19
|
+
# end
|
|
20
|
+
# # passing `thing` to a group item will automatically use it as the base
|
|
21
|
+
# # for item channels
|
|
22
|
+
# group_item Equipment, tags: Semantics::HVAC, thing: "binding:thing"
|
|
23
|
+
# string_item Mode, tags: Semantics::Control, channel: "mode"
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
module Builder
|
|
28
|
+
include Core::EntityLookup
|
|
29
|
+
|
|
30
|
+
self.create_dummy_items = true
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# @!macro def_item_method
|
|
36
|
+
# @!method $1_item(name, label = nil, **kwargs)
|
|
37
|
+
# Create a new $1 item
|
|
38
|
+
# @param name [String, Symbol, Core::Items::Proxy] The name for the new item.
|
|
39
|
+
# Note that you can use a string, a symbol, or even a literal constant name
|
|
40
|
+
# @param label [String] The item label
|
|
41
|
+
# @yieldparam [ItemBuilder] builder Item for further customization
|
|
42
|
+
# @see ItemBuilder#initialize ItemBuilder#initialize for additional arguments.
|
|
43
|
+
def def_item_method(method)
|
|
44
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
45
|
+
def #{method}_item(*args, **kwargs, &block) # def dimmer_item(*args, **kwargs, &block)
|
|
46
|
+
item(#{method.inspect}, *args, **kwargs, &block) # item(:dimmer, *args, **kwargs, &block)
|
|
47
|
+
end # end
|
|
48
|
+
RUBY
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [ColorItem]
|
|
53
|
+
def_item_method(:color)
|
|
54
|
+
# @return [ContactItem]
|
|
55
|
+
def_item_method(:contact)
|
|
56
|
+
# @return [DateTimeItem]
|
|
57
|
+
def_item_method(:date_time)
|
|
58
|
+
# @return [DimmerItem]
|
|
59
|
+
def_item_method(:dimmer)
|
|
60
|
+
# @return [ImageItem]
|
|
61
|
+
def_item_method(:image)
|
|
62
|
+
# @return [LocationItem]
|
|
63
|
+
def_item_method(:location)
|
|
64
|
+
# @return [NumberItem]
|
|
65
|
+
def_item_method(:number)
|
|
66
|
+
# @return [PlayerItem]
|
|
67
|
+
def_item_method(:player)
|
|
68
|
+
# @return [RollershutterItem]
|
|
69
|
+
def_item_method(:rollershutter)
|
|
70
|
+
# @return [StringItem]
|
|
71
|
+
def_item_method(:string)
|
|
72
|
+
# @return [SwitchItem]
|
|
73
|
+
def_item_method(:switch)
|
|
74
|
+
|
|
75
|
+
# Create a new {GroupItem}
|
|
76
|
+
#
|
|
77
|
+
# @!method group_item(name, label = nil, **kwargs)
|
|
78
|
+
# @param name [String] The name for the new item
|
|
79
|
+
# @param label [String] The item label
|
|
80
|
+
# @param (see GroupItemBuilder#initialize)
|
|
81
|
+
# @yieldparam [GroupItemBuilder] builder Item for further customization
|
|
82
|
+
# @return [GroupItem]
|
|
83
|
+
def group_item(*args, **kwargs, &block)
|
|
84
|
+
item = GroupItemBuilder.new(*args, provider: provider, **kwargs)
|
|
85
|
+
item.instance_eval(&block) if block
|
|
86
|
+
result = provider.add(item)
|
|
87
|
+
item.members.each do |i|
|
|
88
|
+
provider.add(i)
|
|
89
|
+
end
|
|
90
|
+
result
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
include DSL
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def item(*args, **kwargs, &block)
|
|
98
|
+
item = ItemBuilder.new(*args, provider: provider, **kwargs)
|
|
99
|
+
item.instance_eval(&block) if block
|
|
100
|
+
r = provider.add(item)
|
|
101
|
+
return Core::Items::Proxy.new(r) if r.is_a?(Item)
|
|
102
|
+
|
|
103
|
+
item
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @!visibility private
|
|
108
|
+
class BaseBuilderDSL
|
|
109
|
+
include Builder
|
|
110
|
+
|
|
111
|
+
# @!visibility private
|
|
112
|
+
class ProviderWrapper
|
|
113
|
+
attr_reader :provider
|
|
114
|
+
|
|
115
|
+
def initialize(provider)
|
|
116
|
+
@provider = provider
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @!visibility private
|
|
120
|
+
def add(builder)
|
|
121
|
+
item = builder.build
|
|
122
|
+
provider.add(item)
|
|
123
|
+
# make sure to add the item to the registry before linking it
|
|
124
|
+
builder.channels.each do |(channel, config)|
|
|
125
|
+
if !channel.include?(":") &&
|
|
126
|
+
(group = builder.groups.find { |g| g.is_a?(GroupItemBuilder) && g.thing })
|
|
127
|
+
thing = group.thing
|
|
128
|
+
channel = "#{thing}:#{channel}"
|
|
129
|
+
end
|
|
130
|
+
Core::Things::Links::Provider.link(item, channel, config)
|
|
131
|
+
end
|
|
132
|
+
item
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
private_constant :ProviderWrapper
|
|
136
|
+
|
|
137
|
+
# @return [org.openhab.core.items.ItemProvider]
|
|
138
|
+
attr_reader :provider
|
|
139
|
+
|
|
140
|
+
def initialize(provider)
|
|
141
|
+
@provider = ProviderWrapper.new(Core::Items::Provider.current(provider))
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# The ItemBuilder DSL allows you to customize an Item
|
|
146
|
+
class ItemBuilder
|
|
147
|
+
# The type of this item
|
|
148
|
+
# @example
|
|
149
|
+
# type #=> :switch
|
|
150
|
+
# @return [Symbol]
|
|
151
|
+
attr_reader :type
|
|
152
|
+
# Item name
|
|
153
|
+
# @return [String]
|
|
154
|
+
attr_accessor :name
|
|
155
|
+
# Item label
|
|
156
|
+
# @return [String, nil]
|
|
157
|
+
attr_accessor :label
|
|
158
|
+
# Unit dimension (for number items only)
|
|
159
|
+
# @return [String, nil]
|
|
160
|
+
attr_accessor :dimension
|
|
161
|
+
# The formatting pattern for the item's state
|
|
162
|
+
# @return [String, nil]
|
|
163
|
+
attr_accessor :format
|
|
164
|
+
# The icon to be associated with the item
|
|
165
|
+
# @return [Symbol, nil]
|
|
166
|
+
attr_accessor :icon
|
|
167
|
+
# Groups to which this item should be added
|
|
168
|
+
# @return [Array<String, GroupItem>]
|
|
169
|
+
attr_reader :groups
|
|
170
|
+
# Tags to apply to this item
|
|
171
|
+
# @return [Array<String, Semantics::Tag>]
|
|
172
|
+
attr_reader :tags
|
|
173
|
+
# Autoupdate setting
|
|
174
|
+
# @return [true, false, nil]
|
|
175
|
+
attr_accessor :autoupdate
|
|
176
|
+
# {Core::Things::ChannelUID Channel} to link the item to
|
|
177
|
+
# @return [String, Core::Things::ChannelUID, nil]
|
|
178
|
+
attr_accessor :channels
|
|
179
|
+
# @return [Core::Items::Metadata::NamespaceHash]
|
|
180
|
+
attr_reader :metadata
|
|
181
|
+
# Initial state
|
|
182
|
+
# @return [Core::Types::State]
|
|
183
|
+
attr_accessor :state
|
|
184
|
+
|
|
185
|
+
class << self
|
|
186
|
+
# @!visibility private
|
|
187
|
+
def item_factory
|
|
188
|
+
@item_factory ||= org.openhab.core.library.CoreItemFactory.new
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
#
|
|
192
|
+
# Convert the given array to an array of strings.
|
|
193
|
+
# Convert Semantics classes to their simple name.
|
|
194
|
+
#
|
|
195
|
+
# @param [String,Symbol,Semantics::Tag] tags A list of strings, symbols, or Semantics classes
|
|
196
|
+
# @return [Array] An array of strings
|
|
197
|
+
#
|
|
198
|
+
# @example
|
|
199
|
+
# tags = normalize_tags("tag1", Semantics::LivingRoom)
|
|
200
|
+
#
|
|
201
|
+
# @!visibility private
|
|
202
|
+
def normalize_tags(*tags)
|
|
203
|
+
semantics = proc { |tag| tag.respond_to?(:java_class) && tag < Semantics::Tag }
|
|
204
|
+
|
|
205
|
+
tags.compact.map do |tag|
|
|
206
|
+
case tag
|
|
207
|
+
when String then tag
|
|
208
|
+
when Symbol then tag.to_s
|
|
209
|
+
when semantics then tag.java_class.simple_name
|
|
210
|
+
else raise ArgumentError, "`#{tag}` must be a subclass of Semantics::Tag, a `Symbol`, or a `String`."
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# @param dimension [Symbol, nil] The unit dimension for a {NumberItem} (see {ItemBuilder#dimension})
|
|
217
|
+
# @param format [String, nil] The formatting pattern for the item's state (see {ItemBuilder#format})
|
|
218
|
+
# @param icon [Symbol, nil] The icon to be associated with the item (see {ItemBuilder#icon})
|
|
219
|
+
# @param group [String,
|
|
220
|
+
# GroupItem,
|
|
221
|
+
# GroupItemBuilder,
|
|
222
|
+
# Array<String, GroupItem, GroupItemBuilder>,
|
|
223
|
+
# nil]
|
|
224
|
+
# Group(s) to which this item should be added (see {ItemBuilder#group}).
|
|
225
|
+
# @param groups [String,
|
|
226
|
+
# GroupItem,
|
|
227
|
+
# GroupItemBuilder,
|
|
228
|
+
# Array<String, GroupItem, GroupItemBuilder>,
|
|
229
|
+
# nil]
|
|
230
|
+
# Fluent alias for `group`.
|
|
231
|
+
# @param tag [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil]
|
|
232
|
+
# Tag(s) to apply to this item (see {ItemBuilder#tag}).
|
|
233
|
+
# @param tags [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil]
|
|
234
|
+
# Fluent alias for `tag`.
|
|
235
|
+
# @param autoupdate [true, false, nil] Autoupdate setting (see {ItemBuilder#autoupdate})
|
|
236
|
+
# @param channel [String, Core::Things::ChannelUID, nil] Channel to link the item to
|
|
237
|
+
# @param expire [String] An expiration specification.
|
|
238
|
+
# @param alexa [String, Symbol, Array<(String, Hash<String, Object>)>, nil]
|
|
239
|
+
# Alexa metadata (see {ItemBuilder#alexa})
|
|
240
|
+
# @param ga [String, Symbol, Array<(String, Hash<String, Object>)>, nil]
|
|
241
|
+
# Google Assistant metadata (see {ItemBuilder#ga})
|
|
242
|
+
# @param homekit [String, Symbol, Array<(String, Hash<String, Object>)>, nil]
|
|
243
|
+
# Homekit metadata (see {ItemBuilder#homekit})
|
|
244
|
+
# @param metadata [Hash<String, Hash>] Generic metadata (see {ItemBuilder#metadata})
|
|
245
|
+
# @param state [State] Initial state
|
|
246
|
+
def initialize(type, name = nil, label = nil,
|
|
247
|
+
provider:,
|
|
248
|
+
dimension: nil,
|
|
249
|
+
format: nil,
|
|
250
|
+
icon: nil,
|
|
251
|
+
group: nil,
|
|
252
|
+
groups: nil,
|
|
253
|
+
tag: nil,
|
|
254
|
+
tags: nil,
|
|
255
|
+
autoupdate: nil,
|
|
256
|
+
channel: nil,
|
|
257
|
+
expire: nil,
|
|
258
|
+
alexa: nil,
|
|
259
|
+
ga: nil, # rubocop:disable Naming/MethodParameterName
|
|
260
|
+
homekit: nil,
|
|
261
|
+
metadata: nil,
|
|
262
|
+
state: nil)
|
|
263
|
+
raise ArgumentError, "`name` cannot be nil" if name.nil?
|
|
264
|
+
raise ArgumentError, "`dimension` can only be specified with NumberItem" if dimension && type != :number
|
|
265
|
+
|
|
266
|
+
name = name.name if name.respond_to?(:name)
|
|
267
|
+
if provider.is_a?(GroupItemBuilder)
|
|
268
|
+
name = "#{provider.name_base}#{name}"
|
|
269
|
+
label = "#{provider.label_base}#{label}".strip if label
|
|
270
|
+
end
|
|
271
|
+
@provider = provider
|
|
272
|
+
@type = type
|
|
273
|
+
@name = name.to_s
|
|
274
|
+
@label = label
|
|
275
|
+
@dimension = dimension
|
|
276
|
+
@format = format
|
|
277
|
+
@icon = icon
|
|
278
|
+
@groups = []
|
|
279
|
+
@tags = []
|
|
280
|
+
@metadata = Core::Items::Metadata::NamespaceHash.new
|
|
281
|
+
@metadata.merge!(metadata) if metadata
|
|
282
|
+
@autoupdate = autoupdate
|
|
283
|
+
@channels = []
|
|
284
|
+
@expire = nil
|
|
285
|
+
if expire
|
|
286
|
+
expire = Array(expire)
|
|
287
|
+
expire_config = expire.pop if expire.last.is_a?(Hash)
|
|
288
|
+
expire_config ||= {}
|
|
289
|
+
self.expire(*expire, **expire_config)
|
|
290
|
+
end
|
|
291
|
+
self.alexa(alexa) if alexa
|
|
292
|
+
self.ga(ga) if ga
|
|
293
|
+
self.homekit(homekit) if homekit
|
|
294
|
+
@state = state
|
|
295
|
+
|
|
296
|
+
self.group(*group)
|
|
297
|
+
self.group(*groups)
|
|
298
|
+
|
|
299
|
+
self.tag(*tag)
|
|
300
|
+
self.tag(*tags)
|
|
301
|
+
|
|
302
|
+
self.channel(*channel) if channel
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
#
|
|
306
|
+
# The item's label if one is defined, otherwise its name.
|
|
307
|
+
#
|
|
308
|
+
# @return [String]
|
|
309
|
+
#
|
|
310
|
+
def to_s
|
|
311
|
+
label || name
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
#
|
|
315
|
+
# Tag item
|
|
316
|
+
#
|
|
317
|
+
# @param tags [String, Symbol, Semantics::Tag]
|
|
318
|
+
# @return [void]
|
|
319
|
+
#
|
|
320
|
+
def tag(*tags)
|
|
321
|
+
@tags += self.class.normalize_tags(*tags)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
#
|
|
325
|
+
# Add this item to a group
|
|
326
|
+
#
|
|
327
|
+
# @param groups [String, GroupItemBuilder, GroupItem]
|
|
328
|
+
# @return [void]
|
|
329
|
+
#
|
|
330
|
+
def group(*groups)
|
|
331
|
+
unless groups.all? do |group|
|
|
332
|
+
group.is_a?(String) || group.is_a?(Core::Items::GroupItem) || group.is_a?(GroupItemBuilder)
|
|
333
|
+
end
|
|
334
|
+
raise ArgumentError, "`group` must be a `GroupItem`, `GroupItemBuilder`, or a `String`"
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
@groups.concat(groups)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
#
|
|
341
|
+
# @!method alexa(value, config = nil)
|
|
342
|
+
# Shortcut for adding Alexa metadata
|
|
343
|
+
#
|
|
344
|
+
# @see https://www.openhab.org/docs/ecosystem/alexa/
|
|
345
|
+
#
|
|
346
|
+
# @param value [String, Symbol] Type of Alexa endpoint
|
|
347
|
+
# @param config [Hash, nil] Additional Alexa configuration
|
|
348
|
+
# @return [void]
|
|
349
|
+
#
|
|
350
|
+
|
|
351
|
+
#
|
|
352
|
+
# @!method ga(value, config = nil)
|
|
353
|
+
# Shortcut for adding Google Assistant metadata
|
|
354
|
+
#
|
|
355
|
+
# @see https://www.openhab.org/docs/ecosystem/google-assistant/
|
|
356
|
+
#
|
|
357
|
+
# @param value [String, Symbol] Type of Google Assistant endpoint
|
|
358
|
+
# @param config [Hash, nil] Additional Google Assistant configuration
|
|
359
|
+
# @return [void]
|
|
360
|
+
#
|
|
361
|
+
|
|
362
|
+
#
|
|
363
|
+
# @!method homekit(value, config = nil)
|
|
364
|
+
# Shortcut for adding Homekit metadata
|
|
365
|
+
#
|
|
366
|
+
# @see https://www.openhab.org/addons/integrations/homekit/
|
|
367
|
+
#
|
|
368
|
+
# @param value [String, Symbol] Type of Homekit accessory or characteristic
|
|
369
|
+
# @param config [Hash, nil] Additional Homekit configuration
|
|
370
|
+
# @return [void]
|
|
371
|
+
#
|
|
372
|
+
|
|
373
|
+
%i[alexa ga homekit].each do |shortcut|
|
|
374
|
+
define_method(shortcut) do |value = nil, config = nil|
|
|
375
|
+
value, config = value if value.is_a?(Array)
|
|
376
|
+
metadata[shortcut] = [value, config]
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
#
|
|
381
|
+
# Add a channel link to this item.
|
|
382
|
+
#
|
|
383
|
+
# @param config [Hash] Additional configuration, such as profile
|
|
384
|
+
# @return [void]
|
|
385
|
+
#
|
|
386
|
+
# @example
|
|
387
|
+
# items.build do
|
|
388
|
+
# date_time_item Bedroom_Light_Updated do
|
|
389
|
+
# channel "hue:0210:1:bulb1:color", profile: "system:timestamp-update"
|
|
390
|
+
# end
|
|
391
|
+
# end
|
|
392
|
+
#
|
|
393
|
+
def channel(channel, config = {})
|
|
394
|
+
@channels << [channel, config]
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
#
|
|
398
|
+
# @!method expire(command: nil, state: nil)
|
|
399
|
+
#
|
|
400
|
+
# Configure item expiration
|
|
401
|
+
#
|
|
402
|
+
# @return [void]
|
|
403
|
+
#
|
|
404
|
+
# @example Get the current expire setting
|
|
405
|
+
# expire
|
|
406
|
+
# @example Clear any expire setting
|
|
407
|
+
# expire nil
|
|
408
|
+
# @example Use a duration
|
|
409
|
+
# expire 5.hours
|
|
410
|
+
# @example Use a string duration
|
|
411
|
+
# expire "5h"
|
|
412
|
+
# @example Set a specific state on expiration
|
|
413
|
+
# expire 5.minutes, NULL
|
|
414
|
+
# expire 5.minutes, state: NULL
|
|
415
|
+
# @example Send a command on expiration
|
|
416
|
+
# expire 5.minutes, command: OFF
|
|
417
|
+
def expire(*args, command: nil, state: nil)
|
|
418
|
+
unless (0..2).cover?(args.length)
|
|
419
|
+
raise ArgumentError,
|
|
420
|
+
"wrong number of arguments (given #{args.length}, expected 0..2)"
|
|
421
|
+
end
|
|
422
|
+
return @expire if args.empty?
|
|
423
|
+
|
|
424
|
+
state = args.last if args.length == 2
|
|
425
|
+
raise ArgumentError, "cannot provide both command and state" if command && state
|
|
426
|
+
|
|
427
|
+
duration = args.first
|
|
428
|
+
return @expire = nil if duration.nil?
|
|
429
|
+
|
|
430
|
+
duration = duration.to_s[2..].downcase if duration.is_a?(Duration)
|
|
431
|
+
state = "'#{state}'" if state.respond_to?(:to_str) && type == :string
|
|
432
|
+
@expire = duration
|
|
433
|
+
@expire += ",state=#{state}" if state
|
|
434
|
+
@expire += ",command=#{command}" if command
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# @!visibility private
|
|
438
|
+
def build
|
|
439
|
+
item = create_item
|
|
440
|
+
item.label = label
|
|
441
|
+
item.category = icon.to_s if icon
|
|
442
|
+
groups.each do |group|
|
|
443
|
+
group = group.name if group.respond_to?(:name)
|
|
444
|
+
item.add_group_name(group.to_s)
|
|
445
|
+
end
|
|
446
|
+
tags.each do |tag|
|
|
447
|
+
item.add_tag(tag)
|
|
448
|
+
end
|
|
449
|
+
item.metadata.merge!(metadata)
|
|
450
|
+
item.metadata["autoupdate"] = autoupdate.to_s unless autoupdate.nil?
|
|
451
|
+
item.metadata["expire"] = expire if expire
|
|
452
|
+
item.metadata["stateDescription"] = { "pattern" => format } if format
|
|
453
|
+
item.state = item.format_update(state) unless state.nil?
|
|
454
|
+
item
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# @return [String]
|
|
458
|
+
def inspect
|
|
459
|
+
s = "#<OpenHAB::Core::Items::#{inspect_type}ItemBuilder#{type_details} #{name} #{label.inspect}"
|
|
460
|
+
s += " category=#{icon.inspect}" if icon
|
|
461
|
+
s += " tags=#{tags.inspect}" unless tags.empty?
|
|
462
|
+
s += " groups=#{groups.map { |g| g.respond_to?(:name) ? g.name : g }.inspect}" unless groups.empty?
|
|
463
|
+
s += " metadata=#{metadata.to_h.inspect}" unless metadata.empty?
|
|
464
|
+
"#{s}>"
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
private
|
|
468
|
+
|
|
469
|
+
# @return [String]
|
|
470
|
+
def inspect_type
|
|
471
|
+
type.to_s.capitalize
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# @return [String, nil]
|
|
475
|
+
def type_details
|
|
476
|
+
":#{dimension}" if dimension
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def create_item
|
|
480
|
+
type = @type.to_s.gsub(/(?:^|_)[a-z]/) { |match| match[-1].upcase }
|
|
481
|
+
type = "#{type}:#{dimension}" if dimension
|
|
482
|
+
self.class.item_factory.create_item(type, name)
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# Allows customizing a group. You can also call any method from {Builder}, and those
|
|
487
|
+
# items will automatically be a member of this group.
|
|
488
|
+
class GroupItemBuilder < ItemBuilder
|
|
489
|
+
include Builder
|
|
490
|
+
|
|
491
|
+
Builder.public_instance_methods.each do |m|
|
|
492
|
+
next unless Builder.instance_method(m).owner == Builder
|
|
493
|
+
|
|
494
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
495
|
+
def #{m}(*args, groups: nil, **kwargs) # def dimmer_item(*args, groups: nil, **kwargs)
|
|
496
|
+
groups ||= [] # groups ||= []
|
|
497
|
+
groups << self # groups << self
|
|
498
|
+
super # super
|
|
499
|
+
end # end
|
|
500
|
+
RUBY
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
FUNCTION_REGEX = /^([a-z]+)(?:\(([a-z]+)(?:,([a-z]+))*\))?/i.freeze
|
|
504
|
+
private_constant :FUNCTION_REGEX
|
|
505
|
+
|
|
506
|
+
# The combiner function for this group
|
|
507
|
+
# @return [String, nil]
|
|
508
|
+
attr_accessor :function
|
|
509
|
+
# A thing to be used as the base for the channel of any member items
|
|
510
|
+
# @return [Core::Things::ThingUID, Core::Things::Thing, String, nil]
|
|
511
|
+
attr_accessor :thing
|
|
512
|
+
# A prefix to be added to the name of any member items
|
|
513
|
+
# @return [String, nil]
|
|
514
|
+
attr_accessor :name_base
|
|
515
|
+
# A prefix to be added to the label of any member items
|
|
516
|
+
# @return [String, nil]
|
|
517
|
+
attr_accessor :label_base
|
|
518
|
+
# Members to be created in this group
|
|
519
|
+
# @return [Array<ItemBuilder>]
|
|
520
|
+
attr_reader :members
|
|
521
|
+
|
|
522
|
+
# @param type [Symbol, nil] The base type for the group
|
|
523
|
+
# @param function [String, nil] The combiner function for this group
|
|
524
|
+
# @param thing [Core::Things::ThingUID, Core::Things::Thing, String, nil]
|
|
525
|
+
# A Thing to be used as the base for the channel for any contained items.
|
|
526
|
+
# @param (see ItemBuilder#initialize)
|
|
527
|
+
def initialize(*args, type: nil, function: nil, thing: nil, **kwargs)
|
|
528
|
+
raise ArgumentError, "invalid function #{function}" if function && !function.match?(FUNCTION_REGEX)
|
|
529
|
+
raise ArgumentError, "state cannot be set on GroupItems" if kwargs[:state]
|
|
530
|
+
|
|
531
|
+
super(type, *args, **kwargs)
|
|
532
|
+
@function = function
|
|
533
|
+
@members = []
|
|
534
|
+
@thing = thing
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
# @!visibility private
|
|
538
|
+
def create_item
|
|
539
|
+
base_item = super if type
|
|
540
|
+
if function
|
|
541
|
+
match = function.match(FUNCTION_REGEX)
|
|
542
|
+
|
|
543
|
+
dto = org.openhab.core.items.dto.GroupFunctionDTO.new
|
|
544
|
+
dto.name = match[1]
|
|
545
|
+
dto.params = match[2..]
|
|
546
|
+
function = org.openhab.core.items.dto.ItemDTOMapper.map_function(base_item, dto)
|
|
547
|
+
Core::Items::GroupItem.new(name, base_item, function)
|
|
548
|
+
else
|
|
549
|
+
Core::Items::GroupItem.new(name, base_item)
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
# @!visibility private
|
|
554
|
+
def add(child_item)
|
|
555
|
+
@members << child_item
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
private
|
|
559
|
+
|
|
560
|
+
# @return [String]
|
|
561
|
+
def inspect_type
|
|
562
|
+
"Group"
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# @return [String, nil]
|
|
566
|
+
def type_details
|
|
567
|
+
r = super
|
|
568
|
+
r = "#{r}:#{function}" if function
|
|
569
|
+
r
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def provider
|
|
573
|
+
self
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
end
|