openhab-scripting 2.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/workflow.yml +327 -0
  3. data/.gitignore +17 -0
  4. data/.java-version +1 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/CHANGELOG.md +113 -0
  8. data/Gemfile +28 -0
  9. data/Gemfile.lock +245 -0
  10. data/Guardfile +35 -0
  11. data/LICENSE +277 -0
  12. data/README.md +23 -0
  13. data/Rakefile +406 -0
  14. data/bin/console +15 -0
  15. data/bin/setup +8 -0
  16. data/config/userdata/config/org/openhab/restauth.config +3 -0
  17. data/cucumber.yml +1 -0
  18. data/docs/_config.yml +135 -0
  19. data/docs/contributing/index.md +47 -0
  20. data/docs/examples/conversions.md +123 -0
  21. data/docs/examples/index.md +61 -0
  22. data/docs/index.md +19 -0
  23. data/docs/installation/index.md +26 -0
  24. data/docs/motivation/index.md +27 -0
  25. data/docs/usage/execution.md +9 -0
  26. data/docs/usage/execution/delay.md +48 -0
  27. data/docs/usage/execution/otherwise.md +30 -0
  28. data/docs/usage/execution/run.md +70 -0
  29. data/docs/usage/execution/triggered.md +48 -0
  30. data/docs/usage/guards.md +51 -0
  31. data/docs/usage/guards/between.md +30 -0
  32. data/docs/usage/guards/not_if.md +41 -0
  33. data/docs/usage/guards/only_if.md +40 -0
  34. data/docs/usage/index.md +11 -0
  35. data/docs/usage/items.md +66 -0
  36. data/docs/usage/items/contact.md +84 -0
  37. data/docs/usage/items/dimmer.md +147 -0
  38. data/docs/usage/items/groups.md +76 -0
  39. data/docs/usage/items/number.md +225 -0
  40. data/docs/usage/items/string.md +49 -0
  41. data/docs/usage/items/switch.md +85 -0
  42. data/docs/usage/misc.md +7 -0
  43. data/docs/usage/misc/actions.md +108 -0
  44. data/docs/usage/misc/duration.md +21 -0
  45. data/docs/usage/misc/gems.md +25 -0
  46. data/docs/usage/misc/logging.md +21 -0
  47. data/docs/usage/misc/metadata.md +128 -0
  48. data/docs/usage/misc/store_states.md +42 -0
  49. data/docs/usage/misc/time_of_day.md +69 -0
  50. data/docs/usage/misc/timers.md +67 -0
  51. data/docs/usage/rule.md +43 -0
  52. data/docs/usage/things.md +29 -0
  53. data/docs/usage/triggers.md +8 -0
  54. data/docs/usage/triggers/changed.md +57 -0
  55. data/docs/usage/triggers/channel.md +54 -0
  56. data/docs/usage/triggers/command.md +69 -0
  57. data/docs/usage/triggers/cron.md +19 -0
  58. data/docs/usage/triggers/every.md +76 -0
  59. data/docs/usage/triggers/updated.md +78 -0
  60. data/lib/openhab.rb +39 -0
  61. data/lib/openhab/configuration.rb +16 -0
  62. data/lib/openhab/core/cron.rb +27 -0
  63. data/lib/openhab/core/debug.rb +34 -0
  64. data/lib/openhab/core/dsl.rb +47 -0
  65. data/lib/openhab/core/dsl/actions.rb +107 -0
  66. data/lib/openhab/core/dsl/entities.rb +103 -0
  67. data/lib/openhab/core/dsl/gems.rb +29 -0
  68. data/lib/openhab/core/dsl/group.rb +91 -0
  69. data/lib/openhab/core/dsl/items/items.rb +39 -0
  70. data/lib/openhab/core/dsl/items/number_item.rb +217 -0
  71. data/lib/openhab/core/dsl/items/string_item.rb +102 -0
  72. data/lib/openhab/core/dsl/monkey_patch/actions/actions.rb +4 -0
  73. data/lib/openhab/core/dsl/monkey_patch/actions/script_thing_actions.rb +22 -0
  74. data/lib/openhab/core/dsl/monkey_patch/events.rb +5 -0
  75. data/lib/openhab/core/dsl/monkey_patch/events/item_command.rb +13 -0
  76. data/lib/openhab/core/dsl/monkey_patch/events/item_state_changed.rb +25 -0
  77. data/lib/openhab/core/dsl/monkey_patch/events/thing_status_info.rb +26 -0
  78. data/lib/openhab/core/dsl/monkey_patch/items/contact_item.rb +54 -0
  79. data/lib/openhab/core/dsl/monkey_patch/items/dimmer_item.rb +125 -0
  80. data/lib/openhab/core/dsl/monkey_patch/items/group_item.rb +27 -0
  81. data/lib/openhab/core/dsl/monkey_patch/items/items.rb +130 -0
  82. data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +259 -0
  83. data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +86 -0
  84. data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +69 -0
  85. data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +46 -0
  86. data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +5 -0
  87. data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +24 -0
  88. data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +41 -0
  89. data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +25 -0
  90. data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +23 -0
  91. data/lib/openhab/core/dsl/monkey_patch/types/types.rb +7 -0
  92. data/lib/openhab/core/dsl/property.rb +85 -0
  93. data/lib/openhab/core/dsl/rule/channel.rb +41 -0
  94. data/lib/openhab/core/dsl/rule/cron.rb +115 -0
  95. data/lib/openhab/core/dsl/rule/guard.rb +99 -0
  96. data/lib/openhab/core/dsl/rule/item.rb +207 -0
  97. data/lib/openhab/core/dsl/rule/rule.rb +374 -0
  98. data/lib/openhab/core/dsl/rule/triggers.rb +77 -0
  99. data/lib/openhab/core/dsl/states.rb +63 -0
  100. data/lib/openhab/core/dsl/things.rb +93 -0
  101. data/lib/openhab/core/dsl/time_of_day.rb +203 -0
  102. data/lib/openhab/core/dsl/timers.rb +85 -0
  103. data/lib/openhab/core/dsl/types/quantity.rb +255 -0
  104. data/lib/openhab/core/dsl/units.rb +41 -0
  105. data/lib/openhab/core/duration.rb +69 -0
  106. data/lib/openhab/core/log.rb +175 -0
  107. data/lib/openhab/core/patch_load_path.rb +7 -0
  108. data/lib/openhab/core/startup_delay.rb +22 -0
  109. data/lib/openhab/osgi.rb +52 -0
  110. data/lib/openhab/version.rb +9 -0
  111. data/openhab-scripting.gemspec +30 -0
  112. data/openhab_rules/warmup.rb +5 -0
  113. metadata +157 -0
@@ -0,0 +1,49 @@
1
+ ---
2
+ layout: default
3
+ title: String
4
+ nav_order: 5
5
+ has_children: false
6
+ parent: Items
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # String Item
11
+
12
+ | Method | Parameters | Description | Example |
13
+ | --------------- | ---------- | -------------------------------------------------------------------------- | ------------------------------------------------ |
14
+ | truthy? | | Item state not UNDEF, not NULL and is not blank ('') when trimmed. | `puts "#{item.name} is truthy" if item.truthy?` |
15
+ | String methods* | | All methods for [Ruby String](https://ruby-doc.org/core-2.5.1/String.html) | `StringOne << StringOne + ' World!'` |
16
+ | blank? | | True if state is UNDEF, NULL, string is empty or contains only whitepspace | `StringOne << StringTwo unless StringTwo.blank?` |
17
+
18
+ * All String methods returns a copy of the current state as a string. Methods that modify a string in place, do not modify the underlying state string.
19
+
20
+
21
+ ## Examples
22
+
23
+ String operations can be performed directly on the StringItem
24
+
25
+ ```ruby
26
+ # StringOne has a current state of "Hello"
27
+ StringOne << StringOne + " World!"
28
+ # StringOne will eventually have a state of 'Hello World!'
29
+
30
+ # Add Number item to 5
31
+ NumberOne << 5 + NumberOne
32
+
33
+ ```
34
+
35
+ String Items can be selected in an enumerable with grep.
36
+
37
+ ```ruby
38
+ # Get all StringItems
39
+ items.grep(StringItem)
40
+ .each { |string| logger.info("#{string.id} is a String Item") }
41
+ ```
42
+
43
+ String Item values can be matched against regular expressions
44
+
45
+ ```ruby
46
+ # Get all Strings that start with an H
47
+ Strings.grep(/^H/)
48
+ .each { |string| logger.info("#{string.id} starts with an H") }
49
+ ```
@@ -0,0 +1,85 @@
1
+ ---
2
+ layout: default
3
+ title: Switch
4
+ nav_order: 1
5
+ has_children: false
6
+ parent: Items
7
+ grand_parent: Usage
8
+ ---
9
+
10
+
11
+ # Switch Item
12
+ This class is aliased to **Switch** so you can compare compare item types using ` item.is_a? Switch or grep(Switch)`
13
+
14
+ | Method | Description | Example |
15
+ | ------- | -------------------------------------------- | ----------------------------------------------- |
16
+ | truthy? | Item is not undefined, not null and is ON | `puts "#{item.name} is truthy" if item.truthy?` |
17
+ | on | Send command to turn item ON | `item.on` |
18
+ | off | Send command to turn item OFF | `item.off` |
19
+ | on? | Returns true if item state == ON | `puts "#{item.name} is on." if item.on?` |
20
+ | off? | Returns true if item state == OFF | `puts "#{item.name} is off." if item.off?` |
21
+ | toggle | Send command to invert the state of the item | `item.toggle` |
22
+ | ! | Return the inverted state of the item | `item << !item` |
23
+
24
+
25
+ Switches respond to `on`, `off`, and `toggle`
26
+
27
+ ```ruby
28
+ # Turn on all switches in a group called Switches
29
+ Switches.each(&:on)
30
+ ```
31
+
32
+ Check state with `off?` and `on?`
33
+
34
+ ```ruby
35
+ # Turn on all switches in a group called Switches that are off
36
+ Switches.select(&:off?).each(&:on)
37
+ ```
38
+
39
+ Switches can be selected in an enumerable with grep.
40
+
41
+ ```ruby
42
+ items.grep(Switch)
43
+ .each { |switch| logger.info("Switch #{switch.id} found") }
44
+ ```
45
+
46
+ Switch states also work in grep.
47
+ ```ruby
48
+ # Log all switch items set to ON
49
+ items.grep(Switch)
50
+ .grep(ON)
51
+ .each { |switch| logger.info("#{switch.id} ON") }
52
+
53
+ # Log all switch items set to OFF
54
+ items.grep(Switch)
55
+ .grep(OFF)
56
+ .each { |switch| logger.info("#{switch.id} OFF") }
57
+ ```
58
+
59
+ Switch states also work in case statements.
60
+ ```ruby
61
+ items.grep(Switch)
62
+ .each do |switch|
63
+ case switch
64
+ when ON
65
+ logger.info("#{switch.id} ON")
66
+ when OFF
67
+ logger.info("#{switch.id} OFF")
68
+ end
69
+ end
70
+ ```
71
+
72
+
73
+ Other examples
74
+ ```ruby
75
+ # Invert all switches
76
+ items.grep(Switch)
77
+ .each { |item| if item.off? then item.on else item.off end}
78
+
79
+ # Or using not operator
80
+
81
+ items.grep(Switch)
82
+ .each { |item| item << !item }
83
+
84
+ ```
85
+
@@ -0,0 +1,7 @@
1
+ ---
2
+ layout: default
3
+ title: Misc
4
+ nav_order: 6
5
+ has_children: true
6
+ parent: Usage
7
+ ---
@@ -0,0 +1,108 @@
1
+ ---
2
+ layout: default
3
+ title: Actions
4
+ nav_order: 2
5
+ has_children: false
6
+ parent: Misc
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Actions
11
+
12
+ All OpenHAB's actions including those provided by add-ons are available, notably:
13
+ * Audio
14
+ * Voice
15
+ * Things
16
+ * Ephemeris
17
+ * Exec
18
+ * HTTP
19
+ * Ping
20
+
21
+ From add-ons, e.g.:
22
+ * Transformation
23
+ * PersistenceExtensions (from Persistence add-on)
24
+ * NotificationAction (from OpenHAB cloud add-on)
25
+
26
+ For convenience, the following methods are implemented:
27
+
28
+ | Method | Parameters | Description |
29
+ | ---------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
30
+ | notify | msg, email: (optional) | When an email is specified, calls NotificationAction.sendNotification. Otherwise, calls NotificationAction.sendBroadcastNotification |
31
+ | say | text, volume: (optional), voice: (optional), sink: (optional) | Calls Voice.say() |
32
+ | play_sound | filename, volume: (optional), sink: (optional) | Calls Audio.playSound() |
33
+
34
+ ## Accessing thing-related actions
35
+ There are two methods to access thing related actions.
36
+
37
+ The first method uses the `action` method and is analogous to how you load actions in the Jython helper libraries. The second method takes advantage of the fact that actions are intrinsically attached to Things.
38
+
39
+ ### actions based
40
+
41
+ | Method | Parameters | Description |
42
+ | ------- | ---------------- | ---------------------------------------------------------- |
43
+ | actions | scope, thing_uid | Return the Action object for the given scope and thing_uid |
44
+
45
+
46
+ ```ruby
47
+ mail = actions('mail', 'mail:smtp:local')
48
+ mail.sendEmail('me@example.com', 'subject', 'message')
49
+ ```
50
+
51
+
52
+ ### things based
53
+
54
+ | Method | Parameters | Description |
55
+ | ------ | ---------- | --------------------------------------------------------- |
56
+ | varies | varies | Action methods delegated from thing to associated actions |
57
+
58
+
59
+ ```ruby
60
+ things['mail:smtp:local'].sendEmail('me@example.com', 'subject', 'message')
61
+ ```
62
+
63
+
64
+ ## Example
65
+
66
+ Run the TTS engine and output the default audio sink. For more information see [Voice](https://www.openhab.org/docs/configuration/multimedia.html#voice)
67
+ ```ruby
68
+ rule 'Say the time every hour' do
69
+ every :hour
70
+ run { say "The time is #{TimeOfDay.now}" }
71
+ end
72
+ ```
73
+
74
+ ```ruby
75
+ rule 'Play an audio file' do
76
+ every :hour
77
+ run { play_sound "beep.mp3", volume: 100 }
78
+ end
79
+ ```
80
+
81
+ Send a broadcast notification via the OpenHAB Cloud
82
+ ```ruby
83
+ rule 'Send an alert' do
84
+ changed Alarm_Triggered, to: ON
85
+ run { notify 'Red Alert!' }
86
+ end
87
+ ```
88
+
89
+ Send an email using the Mail binding
90
+ ```ruby
91
+ rule 'Send an Email' do
92
+ every :day
93
+ run do
94
+ mail = actions('mail', 'mail:smtp:local')
95
+ mail.sendEmail('me@example.com', 'subject', 'message')
96
+ end
97
+ end
98
+ ```
99
+
100
+ Execute a command line
101
+ ```ruby
102
+ rule 'Run a command' do
103
+ every :day
104
+ run do
105
+ Exec.executeCommandLine('/bin/true')
106
+ end
107
+ end
108
+ ```
@@ -0,0 +1,21 @@
1
+ ---
2
+ layout: default
3
+ title: Duration
4
+ nav_order: 3
5
+ has_children: false
6
+ parent: Misc
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Duration
11
+ [Ruby integers](https://ruby-doc.org/core-2.5.0/Integer.html) are extended with several methods to support durations. These methods create a new duration object that is used by the [Every trigger](#Every), the [for option](#Changed) and [timers](#Timers).
12
+
13
+ Extended Methods
14
+
15
+ | Method | Description |
16
+ | --------------------------------- | ------------------------------ |
17
+ | hour or hours | Convert number to hours |
18
+ | minute or minutes | Convert number to minutes |
19
+ | second or seconds | Convert number to seconds |
20
+ | millisecond or milliseconds or ms | Convert number to milliseconds |
21
+
@@ -0,0 +1,25 @@
1
+ ---
2
+ layout: default
3
+ title: Ruby Gems
4
+ nav_order: 2
5
+ has_children: false
6
+ parent: Misc
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Ruby Gems
11
+
12
+ [Bundler](https://bundler.io/) is integrated, enabling any [Ruby gem](https://rubygems.org/) compatible with JRuby to be used within rules. This permits easy access to the vast ecosystem libraries within the ruby community. It would also create easy reuse of automation libraries within the OpenHAB community, any library published as a gem can be easily pulled into rules.
13
+
14
+ Gems are available using the [inline bundler syntax](https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html). The require statement can be omitted.
15
+
16
+
17
+ ```ruby
18
+ gemfile do
19
+ source 'https://rubygems.org'
20
+ gem 'json', require: false
21
+ gem 'nap', '1.1.0', require: 'rest'
22
+ end
23
+
24
+ logger.info("The nap gem is at version #{REST::VERSION}")
25
+ ```
@@ -0,0 +1,21 @@
1
+ ---
2
+ layout: default
3
+ title: Logging
4
+ nav_order: 1
5
+ has_children: false
6
+ parent: Misc
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Logging
11
+
12
+ Logging is available everywhere through the logger object. The name of the rule file is automatically appended to the logger name. Pending [merge](https://github.com/openhab/openhab-core/pull/1885) into the core.
13
+
14
+ ```ruby
15
+ logger.trace('Test logging at trace') # 2020-12-03 18:05:20.903 [TRACE] [jsr223.jruby.log_test ] - Test logging at trace
16
+ logger.debug('Test logging at debug') # 2020-12-03 18:05:32.020 [DEBUG] [jsr223.jruby.log_test ] - Test logging at debug
17
+ logger.warn('Test logging at warn') # 2020-12-03 18:05:41.817 [WARN ] [jsr223.jruby.log_test ] - Test logging at warn
18
+ logger.info('Test logging at info') # Test logging at info
19
+ logger.error('Test logging at error') # 2020-12-03 18:06:02.021 [ERROR] [jsr223.jruby.log_test ] - Test logging at error
20
+ ```
21
+
@@ -0,0 +1,128 @@
1
+ ---
2
+ layout: default
3
+ title: Item Metadata
4
+ nav_order: 1
5
+ has_children: false
6
+ parent: Misc
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Item Metadata
11
+
12
+ Item metadata can be accessed through `Item.meta` using the hash syntax. The `Item.meta` variable is also an [Enumerable](https://ruby-doc.org/core-2.5.8/Enumerable.html).
13
+
14
+ In addition to the Enumerable methods, the following methods are available for `Item.meta`:
15
+ | Method | Parameters | Description | Example |
16
+ | ---------- | ------------------------ | ------------------------------------------ | -------------------------------- |
17
+ | clear | | Deletes all metadata namespaces | `Item1.meta.clear` |
18
+ | delete | namespace | Delete the given namespace | `Item1.meta.delete 'namespace1'` |
19
+ | each | namespace, value, config | Loops through all the item's namespaces | |
20
+ | merge! | **other | Merge a hash or other item's metadata | |
21
+ | namespace? | namespace | Returns true if the given namespace exists | |
22
+
23
+ Metadata configuration is a hash and can be accessed using a subscript of `Item.meta['namespace']`. For example, the following Item metadata
24
+ ```
25
+ Switch Item1 { namespace1="boo" [ config1="foo", config2="bar" ] }
26
+ ```
27
+
28
+ is accessible via:
29
+ ```
30
+ Item1.meta['namespace1']['config1']
31
+ Item1.meta['namespace1']['config2']
32
+ ```
33
+
34
+ The Item namespace has the following methods:
35
+ | Method | Parameters | Description | Example |
36
+ | ------ | ---------- | --------------------------------------- | ------------------------------------------------ |
37
+ | delete | config_key | Delete the given metadata configuration | `Item1.meta['namespace1'].delete` 'config1' |
38
+ | value | | Returns the namespace value | `Item1.meta['namespace1'].value` # returns 'boo' |
39
+ | value= | | Sets namespace value | `Item1.meta['namespace1'].value = 'moo'` |
40
+
41
+
42
+
43
+ ## Examples
44
+
45
+ With the following item definition:
46
+ ```
47
+ Switch Item1 { namespace1="value" [ config1="foo", config2="bar" ] }
48
+ ```
49
+
50
+ ```ruby
51
+ # Check namespace's existence
52
+ Item1.meta['namespace'].nil?
53
+ Item1.meta.key?('namespace')
54
+
55
+ # Access item's metadata value
56
+ Item1.meta['namespace1'].value
57
+
58
+ # Access namespace1's configuration
59
+ Item1.meta['namespace1']['config1']
60
+
61
+ # Set item's metadata value, preserving its config
62
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
63
+ Item1.meta['namespace1'].value = 'new value'
64
+ # Item1's metadata after: { namespace1="new value" [ config1="foo", config2="bar" ] }
65
+
66
+ # Set item's metadata config, preserving its value
67
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
68
+ Item1.meta['namespace1'].config = { 'scooby'=>'doo' }
69
+ # Item1's metadata after: { namespace1="value" [ scooby="doo" ] }
70
+
71
+ # Set a namespace to a new value and config in one line
72
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
73
+ Item1.meta['namespace1'] = 'new value', { 'scooby'=>'doo' }
74
+ # Item1's metadata after: { namespace1="new value" [ scooby="doo" ] }
75
+
76
+ # Set item's metadata value and clear its previous config
77
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
78
+ Item1.meta['namespace1'] = 'new value'
79
+ # Item1's metadata after: { namespace1="value" }
80
+
81
+ # Set item's metadata config, set its value to nil, and wiping out previous config
82
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
83
+ Item1.meta['namespace1'] = { 'newconfig' => 'value' }
84
+ # Item1's metadata after: { namespace1=nil [ config1="foo", config2="bar" ] }
85
+
86
+ # Update namespace1's specific configuration, preserving its value and other config
87
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
88
+ Item1.meta['namespace1']['config1'] = 'doo'
89
+ # Item1's metadata will be: { namespace1="value" [ config1="doo", config2="bar" ] }
90
+
91
+ # Add a new configuration to namespace1
92
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
93
+ Item1.meta['namespace1']['config3'] = 'boo'
94
+ # Item1's metadata after: { namespace1="value" [ config1="foo", config2="bar", config3="boo" ] }
95
+
96
+ # Delete a config
97
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
98
+ Item1.meta['namespace1'].delete('config2')
99
+ # Item1's metadata after: { namespace1="value" [ config1="foo" ] }
100
+
101
+ # Add a namespace and set it to a value
102
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
103
+ Item1.meta['namespace2'] = 'qx'
104
+ # Item1's metadata after: { namespace1="value" [ config1="foo", config2="bar" ], namespace2="qx" }
105
+
106
+ # Add a namespace and set it to a value and config
107
+ # Item1's metadata before: { namespace1="value" [ config1="foo", config2="bar" ] }
108
+ Item1.meta['namespace2'] = 'qx', { 'config1' => 'doo' }
109
+ # Item1's metadata after: { namespace1="value" [ config1="foo", config2="bar" ], namespace2="qx" [ config1="doo" ] }
110
+
111
+ # Enumerate Item1's namespaces
112
+ Item1.meta.each { |namespace, value, config| logger.info("Item1's namespace: #{namespace}='#{value}' #{config}") }
113
+
114
+ # Add metadata from a hash
115
+ Item1.meta.merge!({'namespace1' => [ 'foo', {'config1'=>'baz'} ], 'namespace2' => [ 'qux', {'config'=>'quu'} ]})
116
+
117
+ # Merge Item2's metadata into Item1's metadata
118
+ Item1.meta.merge! Item2.meta
119
+
120
+ # Delete a namespace
121
+ Item1.meta.delete('namespace1')
122
+
123
+ # Delete all metadata of the item
124
+ Item1.meta.clear
125
+
126
+ # Does this item have any metadata?
127
+ Item1.meta.any?
128
+ ```