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,84 @@
1
+ ---
2
+ layout: default
3
+ title: Contact
4
+ nav_order: 3
5
+ has_children: false
6
+ parent: Items
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Contact Item
11
+
12
+ This class is aliased to **Contact** so you can compare compare item types using ` item.is_a? Contact or grep(Contact)`
13
+
14
+
15
+ | Method | Description | Example |
16
+ | ------- | ------------------------------------ | ----------------------------------------- |
17
+ | open? | Returns true if item state == OPEN | `puts "#{item} is closed." if item.open?` |
18
+ | closed? | Returns true if item state == CLOSED | `puts "#{item} is off." if item.closed` |
19
+
20
+
21
+ ##### Examples
22
+
23
+ `open?`/`closed?` checks state of contact
24
+
25
+ ```ruby
26
+ # Log open contacts
27
+ Contacts.select(&:open?).each { |contact| logger.info("Contact #{contact.id} is open")}
28
+
29
+ # Log closed contacts
30
+ Contacts.select(&:closed?).each { |contact| logger.info("Contact #{contact.id} is closed")}
31
+
32
+ ```
33
+
34
+ Contacts can be selected in an enumerable with grep.
35
+
36
+ ```ruby
37
+ # Get all Contacts
38
+ items.grep(Contact)
39
+ .each { |contact| logger.info("#{contact.id} is a Contact") }
40
+ ```
41
+
42
+ Contacts states work in grep.
43
+
44
+ ```ruby
45
+ # Log all open contacts in a group
46
+ Contacts.grep(OPEN)
47
+ .each { |contact| logger.info("#{contact.id} is in #{contact}") }
48
+
49
+ # Log all closed contacts in a group
50
+ Contacts.grep(CLOSED)
51
+ .each { |contact| logger.info("#{contact.id} is in #{contact}") }
52
+
53
+ ```
54
+
55
+ Contact states work in case statements.
56
+
57
+ ```ruby
58
+ #Log if contact is open or closed
59
+ case TestContact
60
+ when (OPEN)
61
+ logger.info("#{TestContact.id} is open")
62
+ when (CLOSED)
63
+ logger.info("#{TestContact.id} is closed")
64
+ end
65
+ ```
66
+
67
+
68
+ Other examples
69
+
70
+ ```ruby
71
+ rule 'Log state of all doors on system startup' do
72
+ on_start
73
+ run do
74
+ Doors.each do |door|
75
+ case door
76
+ when OPEN then logger.info("#{door.id} is Open")
77
+ when CLOSED then logger.info("#{door.id} is Open")
78
+ else logger.info("#{door.id} is not initialized")
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ ```
@@ -0,0 +1,147 @@
1
+ ---
2
+ layout: default
3
+ title: Dimmer
4
+ nav_order: 2
5
+ has_children: false
6
+ parent: Items
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Dimmer
11
+
12
+ DimmerItem is aliased to **Dimmer** so you can compare compare item types using
13
+
14
+ `item.is_a? Dimmer or grep(Dimmer)`
15
+
16
+
17
+ | Method | Parameters | Description | Example |
18
+ | -------- | ------------------ | -------------------------------------------- | ----------------------------------------------- |
19
+ | truthy? | | Item state not UNDEF, not NULL and is ON | `puts "#{item.name} is truthy" if item.truthy?` |
20
+ | on | | Send command to turn item ON | `item.on` |
21
+ | off | | Send command to turn item OFF | `item.off` |
22
+ | on? | | Returns true if item state == ON | `puts "#{item.name} is on." if item.on?` |
23
+ | off? | | Returns true if item state == OFF | `puts "#{item.name} is off." if item.off?` |
24
+ | dim | amount (default 1) | Dim the switch the specified amount | `DimmerSwitch.dim` |
25
+ | - | amount | Subtract the supplied amount from DimmerItem | `DimmerSwitch << DimmerSwitch - 5` |
26
+ | brighten | amount (default 1) | Brighten the switch the specified amount | `DimmerSwitch.brighten` |
27
+ | + | amount | Add the supplied amount from the DimmerItem | `DimmerSwitch << DimmerSwitch + 5` |
28
+
29
+
30
+ ## Examples
31
+
32
+ ```ruby
33
+ DimmerOne << DimmerOne - 5
34
+ DimmerOne << 100 - DimmerOne
35
+
36
+ ```
37
+
38
+ `on`/`off` sends commands to a Dimmer
39
+
40
+ ```ruby
41
+ # Turn on all dimmers in group
42
+ Dimmers.each(&:on)
43
+
44
+ # Turn off all dimmers in group
45
+ Dimmers.each(&:off)
46
+ ```
47
+
48
+ `on?`/`off?` Checks state of dimmer
49
+
50
+ ```ruby
51
+ # Turn on switches that are off
52
+ Dimmers.select(&:off?).each(&:on)
53
+
54
+ # Turn off switches that are on
55
+ Dimmers.select(&:on?).each(&:off)
56
+ ```
57
+
58
+ `dim` dims the specified amount, defaulting to 1. If 1 is the amount, the decrease command is sent, otherwise the current state - amount is sent as a command.
59
+
60
+ ```ruby
61
+ DimmerOne.dim
62
+ DimmerOne.dim 2
63
+ ```
64
+
65
+ `brighten` brightens the specified amount, defaulting to 1. If 1 is the amount, the increase command is sent, otherwise the current state + amount is sent as a command.
66
+
67
+ ```ruby
68
+ DimmerOne.brighten
69
+ DimmerOne.brighten 2
70
+ ```
71
+
72
+ Dimmers can be selected in an enumerable with grep.
73
+
74
+ ```ruby
75
+ # Get all dimmers
76
+ items.grep(Dimmer)
77
+ .each { |dimmer| logger.info("#{dimmer.id} is a Dimmer") }
78
+ ```
79
+
80
+ Dimmers work with ranges and can be used in grep.
81
+
82
+ ```ruby
83
+ # Get dimmers with a state of less than 50
84
+ items.grep(Dimmer)
85
+ .grep(0...50)
86
+ .each { |item| logger.info("#{item.id} is less than 50") }
87
+ ```
88
+
89
+ Dimmers can also be used in case statements with ranges.
90
+ ```ruby
91
+ #Log dimmer states partioning aat 50%
92
+ items.grep(Dimmer)
93
+ .each do |dimmer|
94
+ case dimmer
95
+ when (0..50)
96
+ logger.info("#{dimmer.id} is less than 50%")
97
+ when (51..100)
98
+ logger.info("#{dimmer.id} is greater than 50%")
99
+ end
100
+ end
101
+ ```
102
+
103
+ Other examples
104
+
105
+ ```ruby
106
+ rule 'Dim a switch on system startup over 100 seconds' do
107
+ on_start
108
+ 100.times do
109
+ run { DimmerSwitch.dim }
110
+ delay 1.second
111
+ end
112
+ end
113
+
114
+ ```
115
+
116
+ ```ruby
117
+ rule 'Dim a switch on system startup by 5, pausing every second' do
118
+ on_start
119
+ 100.step(-5, 0) do | level |
120
+ run { DimmerSwitch << level }
121
+ delay 1.second
122
+ end
123
+ end
124
+ ```
125
+
126
+ ```ruby
127
+ rule 'Turn off any dimmers curently on at midnight' do
128
+ every :day
129
+ run do
130
+ items.grep(Dimmer)
131
+ .select(&:on?)
132
+ .each(&:off)
133
+ end
134
+ end
135
+ ```
136
+
137
+ ```ruby
138
+ rule 'Turn off any dimmers set to less than 50 at midnight' do
139
+ every :day
140
+ run do
141
+ items.grep(Dimmer)
142
+ .grep(1...50)
143
+ .each(&:off)
144
+ end
145
+ end
146
+ ```
147
+
@@ -0,0 +1,76 @@
1
+ ---
2
+ layout: default
3
+ title: Groups
4
+ nav_order: 5
5
+ has_children: false
6
+ parent: Items
7
+ grand_parent: Usage
8
+ ---
9
+
10
+ # Groups
11
+
12
+ A group can be accessed directly by name, to access all groups use the `groups` method.
13
+
14
+
15
+ ## Group Methods
16
+
17
+ | Method | Description |
18
+ | ------------------ | ----------------------------------------------------------------------------------------------- |
19
+ | group | Access Group Item |
20
+ | items | Used to inform a rule that you want it to operate on the items in the group (see example below) |
21
+ | groups | Direct subgroups of this group |
22
+ | set methods | All methods [here](https://ruby-doc.org/stdlib-2.5.0/libdoc/set/rdoc/Set.html) |
23
+ | enumerable methods | All methods [here](https://ruby-doc.org/core-2.5.0/Enumerable.html) |
24
+
25
+
26
+ ## Examples
27
+
28
+ Given the following
29
+
30
+ ```
31
+ Group House
32
+ // Location perspective
33
+ Group GroundFloor (House)
34
+ Group Livingroom (GroundFloor)
35
+ // Functional perspective
36
+ Group Sensors (House)
37
+ Group Temperatures (Sensors)
38
+
39
+ Number Livingroom_Temperature "Living Room temperature" (Livingroom, Temperatures)
40
+ Number Bedroom_Temp "Bedroom temperature" (GroundFloor, Temperatures)
41
+ Number Den_Temp "Den temperature" (GroundFloor, Temperatures)
42
+ ```
43
+
44
+ The following are log lines and the output after the comment
45
+
46
+ ```ruby
47
+ #Operate on items in a group using enumerable methods
48
+ logger.info("Total Temperatures: #{Temperatures.count}") #Total Temperatures: 3'
49
+ logger.info("Temperatures: #{House.sort_by(&:label).map(&:label).join(', ')}") #Temperatures: Bedroom temperature, Den temperature, Living Room temperature'
50
+
51
+ #Access to the group object via the 'group' method
52
+ logger.info("Group: #{Temperatures.group.name}" # Group: Temperatures'
53
+
54
+ #Operates on items in nested groups using enumerable methods
55
+ logger.info("House Count: #{House.count}") # House Count: 3
56
+ llogger.info("Items: #{House.sort_by(&:label).map(&:label).join(', ')}") # Items: Bedroom temperature, Den temperature, Living Room temperature
57
+
58
+ #Access to sub groups using the 'groups' method
59
+ logger.info("House Sub Groups: #{House.groups.count}") # House Sub Groups: 2
60
+ logger.info("Groups: #{House.groups.sort_by(&:id).map(&:id).join(', ')}") # Groups: GroundFloor, Sensors
61
+
62
+ ```
63
+
64
+
65
+ ```ruby
66
+ rule 'Turn off any switch that changes' do
67
+ changed Switches.items
68
+ triggered &:off
69
+ end
70
+ ```
71
+
72
+ Built in [enumerable](https://ruby-doc.org/core-2.5.1/Enumerable.html)/[set](https://ruby-doc.org/stdlib-2.5.1/libdoc/set/rdoc/Set.html) functions can be applied to groups.
73
+ ```ruby
74
+ logger.info("Max is #{Temperatures.max}")
75
+ logger.info("Min is #{Temperatures.min}")
76
+ ```
@@ -0,0 +1,225 @@
1
+ ---
2
+ layout: default
3
+ title: Number
4
+ nav_order: 4
5
+ has_children: false
6
+ parent: Items
7
+ grand_parent: Usage
8
+ ---
9
+
10
+
11
+ #### Number Item
12
+
13
+ | Method | Parameters | Description | Example |
14
+ | --------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
15
+ | truthy? | | Item state not UNDEF, not NULL and is not Zero | `puts "#{item.name} is truthy" if item.truthy?` |
16
+ | +,-,\*,/ | amount | Perform the operation between the state of the number item and the supplied value* | `NumberItem << NumberItem - 5` or `NumberItem << 10 + NumberItem` |
17
+ | \| | unit | Convert the supplied NumberItem to the supplied unit. Unit can either be a Unit class or string representation of the symbol, returns a Quantity object. | `NumberItem` &#124; `ImperialUnits::FAHRENHEIT` or `NumberItem `&#124;`'°F'` |
18
+ | to_d | | Returns the state as a BigDecimal or nil if state is UNEF or NULL | `NumberOne.to_d` |
19
+ | to_i | | Returns the state as an Integer or nil if state is UNEF or NULL | `NumberOne.to_i` |
20
+ | to_f | | Returns the state as a Float or nil if state is UNEF or NULL | `NumberOne.to_f` |
21
+ | dimension | | Returns the dimension of the Number Item, nil if the number is dimensionless | `Numberone.dimension` |
22
+ | Numeric Methods | | All methods for [Ruby Numeric](https://ruby-doc.org/core-2.5.0/Numeric.html) | |
23
+
24
+ Math operations for dimensionless numbers return a type of [Ruby BigDecimal](https://ruby-doc.org/stjjdlib-2.5.1/libdoc/bigdecimal/rdoc/BigDecimal.html). Check [Quantities section](#Quantities) for details of how math operations impact dimensioned numbers.
25
+
26
+
27
+ ##### Examples
28
+
29
+ Math operations can be performed directly on the NumberItem
30
+
31
+ ```ruby
32
+ # Add 5 to a number item
33
+ NumberOne << NumberOne + 5
34
+
35
+ # Add Number item to 5
36
+ NumberOne << 5 + NumberOne
37
+
38
+ ```
39
+
40
+ Number Items can be selected in an enumerable with grep.
41
+
42
+ ```ruby
43
+ # Get all NumberItems
44
+ items.grep(NumberItem)
45
+ .each { |number| logger.info("#{number.id} is a Number Item") }
46
+ ```
47
+
48
+ Number Item work with ranges and can be used in grep.
49
+
50
+ ```ruby
51
+ # Get numbers in group Numbers with a state of less than 50
52
+ # Get all NumberItems less than 50
53
+ Numbers.grep(0...50)
54
+ .each { |number| logger.info("#{number.id} is less than 50") }
55
+ ```
56
+
57
+ Number Items can also be used in case statements with ranges.
58
+ ```ruby
59
+ #Check if number items is less than 50
60
+ case NumberOne
61
+ when (0...50)
62
+ logger.info("#{NumberOne.id} is less than 50")
63
+ when (50..100)
64
+ logger.info("#{NumberOne.id} is greater than 50")
65
+ end
66
+ ```
67
+
68
+
69
+ #### Quantities
70
+ Quantities are part of the [Units of Measurement](https://www.openhab.org/docs/concepts/units-of-measurement.html) framework in OpenHAB. The quantity object acts as ruby wrapper around the OpenHAB QuantityType.
71
+
72
+ | Method | Parameters | Description | Example |
73
+ | ------------------ | ---------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
74
+ | +,-,\*,/,-(negate) | amount | Perform the operation between the state of the number item and the supplied value* | `NumberItem << NumberItem - 5` or `NumberItem << 10 + NumberItem` |
75
+ | \| | unit | Convert the supplied Quantity to the supplied unit. Unit can either be a Unit class or string representation of the symbol | `NumberItem` &#124; `ImperialUnits::FAHRENHEIT` or `NumberItem `&#124;`'°F'` |
76
+ | quantity | | Returns the underlying OpenHAB QuantityType object | `Numberone.dimension` |
77
+ | Numeric Methods | | All methods for [Ruby Numeric](https://ruby-doc.org/core-2.5.0/Numeric.html) | |
78
+
79
+ ###### Examples
80
+
81
+ Quantity types can perform math operations between them.
82
+
83
+ ```ruby
84
+ Quantity.new('50 °F') + -Quantity.new('25 °F') = 25.0 °F
85
+ Quantity.new('100 °F') / Quantity.new('2 °F') = 50
86
+ Quantity.new('50 °F') * Quantity.new('2 °F') = 100 °F
87
+ Quantity.new('50 °F') - Quantity.new('25 °F') = 25 °F
88
+ Quantity.new('50 °F') + Quantity.new('50 °F') = 100 °F
89
+ ```
90
+
91
+ If the operand is a string it will be automatically converted into a Quantity.
92
+ ```ruby
93
+ Quantity.new('100 °F') / '2 °F' = 50
94
+ Quantity.new('50 °F') * '2 °F' = 100 °F
95
+ Quantity.new('50 °F') - '25 °F' = 25 °F
96
+ Quantity.new('50 °F') + '50 °F' = 100 °F
97
+ ```
98
+
99
+ If the operand is a number, it will be unit-less, but the result of the operation will have a unit. This only works for multiplication and division.
100
+ ```ruby
101
+ Quantity.new('50 °F') * 2 = 100 °F
102
+ Quantity.new('100 °F') / 2 = 50 °F
103
+ ```
104
+
105
+ If the operand is a dimensioned NumberItem it will automatically be converted to a quantity for the operation.
106
+ ```ruby
107
+ # NumberF = '2 °F'
108
+ # NumberC = '2 °C'
109
+
110
+ Quantity.new('50 °F') + NumberF # = 52.0 °F
111
+ Quantity.new('50 °F') + NumberC # = 85.60 °F
112
+ ```
113
+
114
+ If the operand is a non-dimensioned NumberItem it can be used only in multiplication and division operations.
115
+
116
+ ```ruby
117
+ # Number Dimensionless = 2
118
+
119
+ Quantity.new('50 °F') * Dimensionless # = 100 °F
120
+ Quantity.new('50 °F') / Dimensionless # = 25 °F
121
+ ```
122
+
123
+ Quantities can be compared, if they have comparable units.
124
+ ```ruby
125
+ Quantity.new('50 °F') > Quantity.new('25 °F')
126
+ Quantity.new('50 °F') > Quantity.new('525 °F')
127
+ Quantity.new('50 °F') >= Quantity.new('50 °F')
128
+ Quantity.new('50 °F') == Quantity.new('50 °F')
129
+ Quantity.new('50 °F') < Quantity.new('25 °C')
130
+ ```
131
+
132
+ If the compare-to is a string, it will be automatically converted into a quantity.
133
+ ```ruby
134
+ Quantity.new('50 °F') == '50 °F'
135
+ Quantity.new('50 °F') < '25 °C'
136
+ ```
137
+
138
+ Dimensioned Number Items can be converted to quantities with other units using the \| operator
139
+
140
+ ```ruby
141
+ # NumberC = '23 °C'
142
+
143
+ # Using a unit
144
+ logger.info("In Fahrenheit #{NumberC| ImperialUnits::FAHRENHEIT }")
145
+
146
+ # Using a string
147
+ logger.info("In Fahrenheit #{NumberC | '°F'}")
148
+
149
+ ```
150
+
151
+ Dimensionless Number Items can be converted to quantities with units using the \| operator
152
+
153
+ ```ruby
154
+ # Dimensionless = 70
155
+
156
+ # Using a unit
157
+ logger.info("In Fahrenheit #{Dimensionless| ImperialUnits::FAHRENHEIT }")
158
+
159
+ # Using a string
160
+ logger.info("In Fahrenheit #{Dimensionless | '°F'}")
161
+
162
+ ```
163
+
164
+ Dimensioned Number Items automatically use their units and convert automatically for math operations
165
+
166
+ ```ruby
167
+ # Number:Temperature NumberC = 23 °C
168
+ # Number:Temperature NumberF = 70 °F
169
+
170
+ NumberC - NumberF # = 1.88 °C
171
+ NumberF + NumberC # = 143.40 °F
172
+ ```
173
+
174
+ Dimensionless Number Items can be used for multiplication and division.
175
+
176
+ ```ruby
177
+ # Number Dimensionless = 2
178
+ # Number:Temperature NumberF = 70 °F
179
+
180
+ NumberF * Dimensionless # = 140.0 °F
181
+ NumberF / Dimensionless # = 35.0 °F
182
+ Dimensionless * NumberF # = 140.0 °F
183
+ 2 * NumberF # = 140.0 °F
184
+ ```
185
+
186
+ Comparisons work on dimensioned number items with different, but comparable units.
187
+ ```ruby
188
+ # Number:Temperature NumberC = 23 °C
189
+ # Number:Temperature NumberF = 70 °F
190
+
191
+ NumberC > NumberF # = true
192
+ ```
193
+
194
+ Comparisons work with dimensioned numbers and strings representing quantities
195
+ ```ruby
196
+ # Number:Temperature NumberC = 23 °C
197
+ # Number:Temperature NumberF = 70 °F
198
+
199
+ NumberC > '4 °F' #= true
200
+ NumberC == '23 °C' #= true
201
+ ```
202
+
203
+ For certain unit types, such as temperature, all unit needs to be normalized to the comparator for all operations when combining comparison operators with dimensioned numbers.
204
+
205
+ ```ruby
206
+ (NumberC |'°F') - (NumberF |'°F') < '4 °F'
207
+ ```
208
+
209
+ To facilitate conversion of multiple dimensioned and dimensionless numbers the unit block may be used. The unit block attempts to do the _right thing_ based on the mix of dimensioned and dimensionless items within the block. Specifically all dimensionless items are converted to the supplied unit, except when they are used for multiplication or division.
210
+
211
+ ```ruby
212
+ # Number:Temperature NumberC = 23 °C
213
+ # Number:Temperature NumberF = 70 °F
214
+ # Number Dimensionless = 2
215
+
216
+ unit('°F') { NumberC - NumberF < 4 } #= true
217
+ unit('°F') { NumberC - '24 °C' < 4 } #= true
218
+ unit('°F') { Quantity.new('24 °C') - NumberC < 4 } #= true
219
+ unit('°C') { NumberF - '20 °C' < 2 } #= true
220
+ unit('°C') { NumberF - Dimensionless } #= 19.11 °C
221
+ unit('°C') { NumberF - Dimensionless < 20 } #= true
222
+ unit('°C') { Dimensionless + NumberC == 25 } #= true unit('°C') { 2 + NumberC == 25 } #= true
223
+ unit('°C') { Dimensionless * NumberC == 46 } #= true unit('°C') { 2 * NumberC == 46 } #= true
224
+ unit('°C') { ( (2 * (NumberF + NumberC) ) / Dimensionless ) < 45} #= true unit('°C') { [NumberC, NumberF, Dimensionless].min } #= 2
225
+ ```