openhab-jrubyscripting 5.0.0.rc1
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 +7 -0
- data/lib/openhab/core/actions.rb +163 -0
- data/lib/openhab/core/entity_lookup.rb +144 -0
- data/lib/openhab/core/events/abstract_event.rb +17 -0
- data/lib/openhab/core/events/item_channel_link.rb +36 -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 +52 -0
- data/lib/openhab/core/events/item_state_event.rb +51 -0
- data/lib/openhab/core/events/thing.rb +29 -0
- data/lib/openhab/core/events/thing_status_info_event.rb +53 -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 +58 -0
- data/lib/openhab/core/items/dimmer_item.rb +148 -0
- data/lib/openhab/core/items/generic_item.rb +344 -0
- data/lib/openhab/core/items/group_item.rb +174 -0
- data/lib/openhab/core/items/image_item.rb +109 -0
- data/lib/openhab/core/items/location_item.rb +34 -0
- data/lib/openhab/core/items/metadata/hash.rb +390 -0
- data/lib/openhab/core/items/metadata/namespace_hash.rb +469 -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 +327 -0
- data/lib/openhab/core/items/player_item.rb +66 -0
- data/lib/openhab/core/items/proxy.rb +59 -0
- data/lib/openhab/core/items/registry.rb +66 -0
- data/lib/openhab/core/items/rollershutter_item.rb +68 -0
- data/lib/openhab/core/items/semantics/enumerable.rb +152 -0
- data/lib/openhab/core/items/semantics.rb +476 -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 +114 -0
- data/lib/openhab/core/lazy_array.rb +52 -0
- data/lib/openhab/core/profile_factory.rb +118 -0
- data/lib/openhab/core/script_handling.rb +55 -0
- 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/profile_callback.rb +52 -0
- data/lib/openhab/core/things/proxy.rb +69 -0
- data/lib/openhab/core/things/registry.rb +46 -0
- data/lib/openhab/core/things/thing.rb +194 -0
- data/lib/openhab/core/things.rb +22 -0
- data/lib/openhab/core/timer.rb +128 -0
- data/lib/openhab/core/types/comparable_type.rb +23 -0
- data/lib/openhab/core/types/date_time_type.rb +259 -0
- data/lib/openhab/core/types/decimal_type.rb +192 -0
- data/lib/openhab/core/types/hsb_type.rb +183 -0
- 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/core/types/numeric_type.rb +52 -0
- 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/core/types/percent_type.rb +95 -0
- 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 +327 -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/core/types/string_type.rb +76 -0
- data/lib/openhab/core/types/type.rb +117 -0
- 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 +69 -0
- data/lib/openhab/core/uid.rb +36 -0
- data/lib/openhab/core.rb +85 -0
- data/lib/openhab/core_ext/java/duration.rb +115 -0
- data/lib/openhab/core_ext/java/local_date.rb +93 -0
- data/lib/openhab/core_ext/java/local_time.rb +106 -0
- data/lib/openhab/core_ext/java/month.rb +59 -0
- data/lib/openhab/core_ext/java/month_day.rb +105 -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 +58 -0
- data/lib/openhab/core_ext/java/unit.rb +15 -0
- data/lib/openhab/core_ext/java/zoned_date_time.rb +116 -0
- data/lib/openhab/core_ext/ruby/array.rb +21 -0
- data/lib/openhab/core_ext/ruby/class.rb +15 -0
- data/lib/openhab/core_ext/ruby/date.rb +89 -0
- data/lib/openhab/core_ext/ruby/numeric.rb +190 -0
- data/lib/openhab/core_ext/ruby/range.rb +70 -0
- data/lib/openhab/core_ext/ruby/time.rb +104 -0
- data/lib/openhab/core_ext.rb +18 -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 +3 -0
- data/lib/openhab/dsl/items/builder.rb +618 -0
- data/lib/openhab/dsl/items/ensure.rb +93 -0
- data/lib/openhab/dsl/items/timed_command.rb +236 -0
- data/lib/openhab/dsl/rules/automation_rule.rb +308 -0
- data/lib/openhab/dsl/rules/builder.rb +1373 -0
- data/lib/openhab/dsl/rules/guard.rb +115 -0
- data/lib/openhab/dsl/rules/name_inference.rb +160 -0
- data/lib/openhab/dsl/rules/property.rb +76 -0
- data/lib/openhab/dsl/rules/rule_triggers.rb +96 -0
- data/lib/openhab/dsl/rules/terse.rb +63 -0
- data/lib/openhab/dsl/rules/triggers/changed.rb +169 -0
- data/lib/openhab/dsl/rules/triggers/channel.rb +57 -0
- data/lib/openhab/dsl/rules/triggers/command.rb +107 -0
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +161 -0
- data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +164 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +195 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +127 -0
- data/lib/openhab/dsl/rules/triggers/trigger.rb +56 -0
- data/lib/openhab/dsl/rules/triggers/updated.rb +130 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch.rb +55 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +155 -0
- data/lib/openhab/dsl/rules/triggers.rb +12 -0
- data/lib/openhab/dsl/rules.rb +29 -0
- data/lib/openhab/dsl/script_handling.rb +55 -0
- data/lib/openhab/dsl/things/builder.rb +263 -0
- data/lib/openhab/dsl/thread_local.rb +48 -0
- data/lib/openhab/dsl/timer_manager.rb +191 -0
- data/lib/openhab/dsl/version.rb +9 -0
- data/lib/openhab/dsl.rb +686 -0
- data/lib/openhab/log.rb +348 -0
- data/lib/openhab/osgi.rb +70 -0
- data/lib/openhab/rspec/configuration.rb +56 -0
- data/lib/openhab/rspec/example_group.rb +90 -0
- data/lib/openhab/rspec/helpers.rb +439 -0
- data/lib/openhab/rspec/hooks.rb +93 -0
- data/lib/openhab/rspec/jruby.rb +46 -0
- data/lib/openhab/rspec/karaf.rb +811 -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/metadata_provider.rb +75 -0
- data/lib/openhab/rspec/mocks/persistence_service.rb +140 -0
- data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
- data/lib/openhab/rspec/mocks/synchronous_executor.rb +56 -0
- data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
- data/lib/openhab/rspec/mocks/timer.rb +95 -0
- data/lib/openhab/rspec/openhab/core/actions.rb +26 -0
- data/lib/openhab/rspec/openhab/core/items/proxy.rb +27 -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 +60 -0
- data/lib/openhab/rspec.rb +17 -0
- data/lib/openhab/yard/cli/stats.rb +23 -0
- data/lib/openhab/yard/code_objects/group_object.rb +17 -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/handlers/jruby/base.rb +49 -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 +27 -0
- data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
- data/lib/openhab/yard/html_helper.rb +44 -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 +32 -0
- metadata +504 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenHAB
|
|
4
|
+
module DSL
|
|
5
|
+
#
|
|
6
|
+
# Contains extensions to simplify working with [GenericItem]s.
|
|
7
|
+
#
|
|
8
|
+
module Items
|
|
9
|
+
# Stores all items created in scripts, and notifies the ItemRegistry
|
|
10
|
+
# of their existence
|
|
11
|
+
# @!visibility private
|
|
12
|
+
class ItemProvider < org.openhab.core.common.registry.AbstractProvider
|
|
13
|
+
include org.openhab.core.items.ItemProvider
|
|
14
|
+
include Singleton
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
super
|
|
18
|
+
|
|
19
|
+
@items = {}
|
|
20
|
+
|
|
21
|
+
$ir.add_provider(self)
|
|
22
|
+
ScriptHandling.script_unloaded { $ir.remove_provider(self) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Add an item to this provider
|
|
26
|
+
def add(builder)
|
|
27
|
+
item = builder.build
|
|
28
|
+
raise "Item #{item.name} already exists" if @items.key?(item.name)
|
|
29
|
+
|
|
30
|
+
@items[item.name] = item
|
|
31
|
+
notify_listeners_about_added_element(item)
|
|
32
|
+
|
|
33
|
+
# make sure to add the item to the registry before linking it
|
|
34
|
+
builder.channels.each do |(channel, config)|
|
|
35
|
+
if !channel.include?(":") &&
|
|
36
|
+
(group = builder.groups.find { |g| g.is_a?(GroupItemBuilder) && g.thing })
|
|
37
|
+
thing = group.thing
|
|
38
|
+
thing = thing.uid if thing.is_a?(Core::Things::Thing)
|
|
39
|
+
channel = "#{thing}:#{channel}"
|
|
40
|
+
end
|
|
41
|
+
ItemChannelLinkProvider.instance.link(item, channel, config)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
item
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Remove an item from this provider
|
|
48
|
+
#
|
|
49
|
+
# @return [GenericItem, nil] The removed item, if found.
|
|
50
|
+
def remove(item_name, recursive: false)
|
|
51
|
+
return nil unless @items.key?(item_name)
|
|
52
|
+
|
|
53
|
+
item = @items.delete(item_name)
|
|
54
|
+
if recursive && item.is_a?(Core::Items::GroupItem)
|
|
55
|
+
item.members.each { |member| remove(member.__getobj__, recursive: true) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
notify_listeners_about_removed_element(item)
|
|
59
|
+
item
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Get all items in this provider
|
|
63
|
+
def getAll # rubocop:disable Naming/MethodName required by java interface
|
|
64
|
+
@items.values
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @!visibility private
|
|
69
|
+
class ItemChannelLinkProvider < org.openhab.core.common.registry.AbstractProvider
|
|
70
|
+
include org.openhab.core.thing.link.ItemChannelLinkProvider
|
|
71
|
+
include Singleton
|
|
72
|
+
|
|
73
|
+
def initialize
|
|
74
|
+
super
|
|
75
|
+
|
|
76
|
+
@links = Hash.new { |h, k| h[k] = Set.new }
|
|
77
|
+
registry = OSGi.service("org.openhab.core.thing.link.ItemChannelLinkRegistry")
|
|
78
|
+
registry.add_provider(self)
|
|
79
|
+
ScriptHandling.script_unloaded { registry.remove_provider(self) }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @!visibility private
|
|
83
|
+
def link(item, channel, config = {})
|
|
84
|
+
config = org.openhab.core.config.core.Configuration.new(config.transform_keys(&:to_s))
|
|
85
|
+
channel = org.openhab.core.thing.ChannelUID.new(channel) if channel.is_a?(String)
|
|
86
|
+
channel = channel.uid if channel.is_a?(org.openhab.core.thing.Channel)
|
|
87
|
+
link = org.openhab.core.thing.link.ItemChannelLink.new(item.name, channel, config)
|
|
88
|
+
|
|
89
|
+
item_links = @links[item.name]
|
|
90
|
+
if item_links.include?(link)
|
|
91
|
+
notify_listeners_about_updated_element(link, link)
|
|
92
|
+
else
|
|
93
|
+
item_links << link
|
|
94
|
+
notify_listeners_about_added_element(link)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def getAll # rubocop:disable Naming/MethodName required by java interface
|
|
99
|
+
@links.values.flatten
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# An item builder allows you to dynamically create OpenHAB items at runtime.
|
|
104
|
+
# This can be useful either to create items as soon as the script loads,
|
|
105
|
+
# or even later based on a rule executing.
|
|
106
|
+
#
|
|
107
|
+
# @example
|
|
108
|
+
# items.build do
|
|
109
|
+
# switch_item "MySwitch", "My Switch"
|
|
110
|
+
# switch_item "NotAutoupdating", autoupdate: false, channel: "mqtt:topic:1#light"
|
|
111
|
+
# group_item "MyGroup" do
|
|
112
|
+
# contact_item "ItemInGroup", channel: "binding:thing#channel"
|
|
113
|
+
# end
|
|
114
|
+
# # passing `thing` to a group item will automatically use it as the base
|
|
115
|
+
# # for item channels
|
|
116
|
+
# group_item "Equipment", tags: Semantics::HVAC, thing: "binding:thing"
|
|
117
|
+
# string_item "Mode", tags: Semantics::Control, channel: "mode"
|
|
118
|
+
# end
|
|
119
|
+
# end
|
|
120
|
+
module Builder
|
|
121
|
+
include Core::EntityLookup
|
|
122
|
+
|
|
123
|
+
class << self
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
# @!macro def_item_method
|
|
127
|
+
# @!method $1_item(name, label = nil, **kwargs)
|
|
128
|
+
# Create a new $1 item
|
|
129
|
+
# @param name [String] The name for the new item
|
|
130
|
+
# @param label [String] The item label
|
|
131
|
+
# @yieldparam [ItemBuilder] builder Item for further customization
|
|
132
|
+
# @see ItemBuilder#initialize ItemBuilder#initialize for additional arguments.
|
|
133
|
+
def def_item_method(method)
|
|
134
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
135
|
+
def #{method}_item(*args, **kwargs, &block) # def dimmer_item(*args, **kwargs, &block)
|
|
136
|
+
item(#{method.inspect}, *args, **kwargs, &block) # item(:dimmer, *args, **kwargs, &block)
|
|
137
|
+
end
|
|
138
|
+
RUBY
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @return [ColorItem]
|
|
143
|
+
def_item_method(:color)
|
|
144
|
+
# @return [ContactItem]
|
|
145
|
+
def_item_method(:contact)
|
|
146
|
+
# @return [DateTimeItem]
|
|
147
|
+
def_item_method(:date_time)
|
|
148
|
+
# @return [DimmerItem]
|
|
149
|
+
def_item_method(:dimmer)
|
|
150
|
+
# @return [ImageItem]
|
|
151
|
+
def_item_method(:image)
|
|
152
|
+
# @return [LocationItem]
|
|
153
|
+
def_item_method(:location)
|
|
154
|
+
# @return [NumberItem]
|
|
155
|
+
def_item_method(:number)
|
|
156
|
+
# @return [PlayerItem]
|
|
157
|
+
def_item_method(:player)
|
|
158
|
+
# @return [RollershutterItem]
|
|
159
|
+
def_item_method(:rollershutter)
|
|
160
|
+
# @return [StringItem]
|
|
161
|
+
def_item_method(:string)
|
|
162
|
+
# @return [SwitchItem]
|
|
163
|
+
def_item_method(:switch)
|
|
164
|
+
|
|
165
|
+
# Create a new {GroupItem}
|
|
166
|
+
#
|
|
167
|
+
# @!method group_item(name, label = nil, **kwargs)
|
|
168
|
+
# @param name [String] The name for the new item
|
|
169
|
+
# @param label [String] The item label
|
|
170
|
+
# @param (see GroupItemBuilder#initialize)
|
|
171
|
+
# @yieldparam [GroupItemBuilder] builder Item for further customization
|
|
172
|
+
# @return [GroupItem]
|
|
173
|
+
def group_item(*args, **kwargs, &block)
|
|
174
|
+
item = GroupItemBuilder.new(*args, provider: provider, **kwargs)
|
|
175
|
+
item.instance_eval(&block) if block
|
|
176
|
+
result = provider.add(item)
|
|
177
|
+
item.members.each do |i|
|
|
178
|
+
provider.add(i)
|
|
179
|
+
end
|
|
180
|
+
result
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
include DSL
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
def item(*args, **kwargs, &block)
|
|
188
|
+
item = ItemBuilder.new(*args, provider: provider, **kwargs)
|
|
189
|
+
item.instance_eval(&block) if block
|
|
190
|
+
provider.add(item)
|
|
191
|
+
Core::Items::Proxy.new(item)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# @!visibility private
|
|
196
|
+
class BaseBuilderDSL
|
|
197
|
+
include Builder
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
|
|
201
|
+
def provider
|
|
202
|
+
ItemProvider.instance
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# The ItemBuilder DSL allows you to customize an Item
|
|
207
|
+
class ItemBuilder
|
|
208
|
+
# The type of this item
|
|
209
|
+
# @example
|
|
210
|
+
# type #=> :switch
|
|
211
|
+
# @return [Symbol]
|
|
212
|
+
attr_reader :type
|
|
213
|
+
# Item name
|
|
214
|
+
# @return [String]
|
|
215
|
+
attr_accessor :name
|
|
216
|
+
# Item label
|
|
217
|
+
# @return [String, nil]
|
|
218
|
+
attr_accessor :label
|
|
219
|
+
# Unit dimension (for number items only)
|
|
220
|
+
# @return [String, nil]
|
|
221
|
+
attr_accessor :dimension
|
|
222
|
+
# The formatting pattern for the item's state
|
|
223
|
+
# @return [String, nil]
|
|
224
|
+
attr_accessor :format
|
|
225
|
+
# The icon to be associated with the item
|
|
226
|
+
# @return [Symbol, nil]
|
|
227
|
+
attr_accessor :icon
|
|
228
|
+
# Groups to which this item should be added
|
|
229
|
+
# @return [Array<String, GroupItem>]
|
|
230
|
+
attr_reader :groups
|
|
231
|
+
# Tags to apply to this item
|
|
232
|
+
# @return [Array<String, Semantics::Tag>]
|
|
233
|
+
attr_reader :tags
|
|
234
|
+
# Autoupdate setting
|
|
235
|
+
# @return [true, false, nil]
|
|
236
|
+
attr_accessor :autoupdate
|
|
237
|
+
# {Core::Things::ChannelUID Channel} to link the item to
|
|
238
|
+
# @return [String, Core::Things::ChannelUID, nil]
|
|
239
|
+
attr_accessor :channels
|
|
240
|
+
# @return [Core::Items::Metadata::NamespaceHash]
|
|
241
|
+
attr_reader :metadata
|
|
242
|
+
# Initial state
|
|
243
|
+
# @return [Core::Types::State]
|
|
244
|
+
attr_accessor :state
|
|
245
|
+
|
|
246
|
+
class << self
|
|
247
|
+
# @!visibility private
|
|
248
|
+
def item_factory
|
|
249
|
+
@item_factory ||= org.openhab.core.library.CoreItemFactory.new
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# @param dimension [Symbol, nil] The unit dimension for a {NumberItem} (see {ItemBuilder#dimension})
|
|
254
|
+
# @param format [String, nil] The formatting pattern for the item's state (see {ItemBuilder#format})
|
|
255
|
+
# @param icon [Symbol, nil] The icon to be associated with the item (see {ItemBuilder#icon})
|
|
256
|
+
# @param group [String,
|
|
257
|
+
# GroupItem,
|
|
258
|
+
# GroupItemBuilder,
|
|
259
|
+
# Array<String, GroupItem, GroupItemBuilder>,
|
|
260
|
+
# nil]
|
|
261
|
+
# Group(s) to which this item should be added (see {ItemBuilder#group}).
|
|
262
|
+
# @param groups [String,
|
|
263
|
+
# GroupItem,
|
|
264
|
+
# GroupItemBuilder,
|
|
265
|
+
# Array<String, GroupItem, GroupItemBuilder>,
|
|
266
|
+
# nil]
|
|
267
|
+
# Fluent alias for `group`.
|
|
268
|
+
# @param tag [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil]
|
|
269
|
+
# Tag(s) to apply to this item (see {ItemBuilder#tag}).
|
|
270
|
+
# @param tags [String, Symbol, Semantics::Tag, Array<String, Symbol, Semantics::Tag>, nil]
|
|
271
|
+
# Fluent alias for `tag`.
|
|
272
|
+
# @param autoupdate [true, false, nil] Autoupdate setting (see {ItemBuilder#autoupdate})
|
|
273
|
+
# @param channel [String, Core::Things::ChannelUID, nil] Channel to link the item to
|
|
274
|
+
# @param expire [String] An expiration specification.
|
|
275
|
+
# @param alexa [String, Symbol, Array<(String, Hash<String, Object>)>, nil]
|
|
276
|
+
# Alexa metadata (see {ItemBuilder#alexa})
|
|
277
|
+
# @param ga [String, Symbol, Array<(String, Hash<String, Object>)>, nil]
|
|
278
|
+
# Google Assistant metadata (see {ItemBuilder#ga})
|
|
279
|
+
# @param homekit [String, Symbol, Array<(String, Hash<String, Object>)>, nil]
|
|
280
|
+
# Homekit metadata (see {ItemBuilder#homekit})
|
|
281
|
+
# @param metadata [Hash<String, Hash>] Generic metadata (see {ItemBuilder#metadata})
|
|
282
|
+
# @param state [State] Initial state
|
|
283
|
+
def initialize(type, name = nil, label = nil,
|
|
284
|
+
provider:,
|
|
285
|
+
dimension: nil,
|
|
286
|
+
format: nil,
|
|
287
|
+
icon: nil,
|
|
288
|
+
group: nil,
|
|
289
|
+
groups: nil,
|
|
290
|
+
tag: nil,
|
|
291
|
+
tags: nil,
|
|
292
|
+
autoupdate: nil,
|
|
293
|
+
channel: nil,
|
|
294
|
+
expire: nil,
|
|
295
|
+
alexa: nil,
|
|
296
|
+
ga: nil, # rubocop:disable Naming/MethodParameterName
|
|
297
|
+
homekit: nil,
|
|
298
|
+
metadata: nil,
|
|
299
|
+
state: nil)
|
|
300
|
+
raise ArgumentError, "`name` cannot be nil" if name.nil?
|
|
301
|
+
raise ArgumentError, "`dimension` can only be specified with NumberItem" if dimension && type != :number
|
|
302
|
+
|
|
303
|
+
if provider.is_a?(GroupItemBuilder)
|
|
304
|
+
name = "#{provider.name_base}#{name}"
|
|
305
|
+
label = "#{provider.label_base}#{label}".strip if label
|
|
306
|
+
end
|
|
307
|
+
@provider = provider
|
|
308
|
+
@type = type
|
|
309
|
+
@name = name.to_s
|
|
310
|
+
@label = label
|
|
311
|
+
@dimension = dimension
|
|
312
|
+
@format = format
|
|
313
|
+
@icon = icon
|
|
314
|
+
@groups = []
|
|
315
|
+
@tags = []
|
|
316
|
+
@metadata = Core::Items::Metadata::NamespaceHash.new
|
|
317
|
+
@metadata.merge!(metadata) if metadata
|
|
318
|
+
@autoupdate = autoupdate
|
|
319
|
+
@channels = []
|
|
320
|
+
@expire = nil
|
|
321
|
+
self.expire(*Array(expire)) if expire
|
|
322
|
+
self.alexa(alexa) if alexa
|
|
323
|
+
self.ga(ga) if ga
|
|
324
|
+
self.homekit(homekit) if homekit
|
|
325
|
+
@state = state
|
|
326
|
+
|
|
327
|
+
self.group(*group)
|
|
328
|
+
self.group(*groups)
|
|
329
|
+
|
|
330
|
+
self.tag(*tag)
|
|
331
|
+
self.tag(*tags)
|
|
332
|
+
|
|
333
|
+
self.channel(*channel) if channel
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
#
|
|
337
|
+
# The item's label if one is defined, otherwise its name.
|
|
338
|
+
#
|
|
339
|
+
# @return [String]
|
|
340
|
+
#
|
|
341
|
+
def to_s
|
|
342
|
+
label || name
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
#
|
|
346
|
+
# Tag item
|
|
347
|
+
#
|
|
348
|
+
# @param tags [String, Symbol, Semantics::Tag]
|
|
349
|
+
# @return [void]
|
|
350
|
+
#
|
|
351
|
+
def tag(*tags)
|
|
352
|
+
unless tags.all? do |tag|
|
|
353
|
+
tag.is_a?(String) ||
|
|
354
|
+
tag.is_a?(Symbol) ||
|
|
355
|
+
(tag.is_a?(Module) && tag < Semantics::Tag)
|
|
356
|
+
end
|
|
357
|
+
raise ArgumentError, "`tag` must be a subclass of Semantics::Tag, or a `String``."
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
tags.each do |tag|
|
|
361
|
+
tag = tag.name.split("::").last if tag.is_a?(Module) && tag < Semantics::Tag
|
|
362
|
+
@tags << tag.to_s
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
#
|
|
367
|
+
# Add this item to a group
|
|
368
|
+
#
|
|
369
|
+
# @param groups [String, GroupItemBuilder, GroupItem]
|
|
370
|
+
# @return [void]
|
|
371
|
+
#
|
|
372
|
+
def group(*groups)
|
|
373
|
+
unless groups.all? do |group|
|
|
374
|
+
group.is_a?(String) || group.is_a?(Core::Items::GroupItem) || group.is_a?(GroupItemBuilder)
|
|
375
|
+
end
|
|
376
|
+
raise ArgumentError, "`group` must be a `GroupItem`, `GroupItemBuilder`, or a `String`"
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
@groups.concat(groups)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
#
|
|
383
|
+
# @!method alexa(value, config = nil)
|
|
384
|
+
# Shortcut for adding Alexa metadata
|
|
385
|
+
#
|
|
386
|
+
# @see https://www.openhab.org/docs/ecosystem/alexa/
|
|
387
|
+
#
|
|
388
|
+
# @param value [String, Symbol] Type of Alexa endpoint
|
|
389
|
+
# @param config [Hash, nil] Additional Alexa configuration
|
|
390
|
+
# @return [void]
|
|
391
|
+
#
|
|
392
|
+
|
|
393
|
+
#
|
|
394
|
+
# @!method ga(value, config = nil)
|
|
395
|
+
# Shortcut for adding Google Assistant metadata
|
|
396
|
+
#
|
|
397
|
+
# @see https://www.openhab.org/docs/ecosystem/google-assistant/
|
|
398
|
+
#
|
|
399
|
+
# @param value [String, Symbol] Type of Google Assistant endpoint
|
|
400
|
+
# @param config [Hash, nil] Additional Google Assistant configuration
|
|
401
|
+
# @return [void]
|
|
402
|
+
#
|
|
403
|
+
|
|
404
|
+
#
|
|
405
|
+
# @!method homekit(value, config = nil)
|
|
406
|
+
# Shortcut for adding Homekit metadata
|
|
407
|
+
#
|
|
408
|
+
# @see https://www.openhab.org/addons/integrations/homekit/
|
|
409
|
+
#
|
|
410
|
+
# @param value [String, Symbol] Type of Homekit accessory or characteristic
|
|
411
|
+
# @param config [Hash, nil] Additional Homekit configuration
|
|
412
|
+
# @return [void]
|
|
413
|
+
#
|
|
414
|
+
|
|
415
|
+
%i[alexa ga homekit].each do |shortcut|
|
|
416
|
+
define_method(shortcut) do |value = nil, config = nil|
|
|
417
|
+
value, config = value if value.is_a?(Array)
|
|
418
|
+
metadata[shortcut] = [value, config]
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
#
|
|
423
|
+
# Add a channel link to this item.
|
|
424
|
+
#
|
|
425
|
+
# @param config [Hash] Additional configuration, such as profile
|
|
426
|
+
# @return [void]
|
|
427
|
+
#
|
|
428
|
+
# @example
|
|
429
|
+
# items.build do
|
|
430
|
+
# date_time_item "Bedroom_Light_Updated" do
|
|
431
|
+
# channel "hue:0210:1:bulb1:color", profile: "system:timestamp-update"
|
|
432
|
+
# end
|
|
433
|
+
# end
|
|
434
|
+
#
|
|
435
|
+
def channel(channel, **config)
|
|
436
|
+
@channels << [channel, config]
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
#
|
|
440
|
+
# @!method expire(command: nil, state: nil)
|
|
441
|
+
#
|
|
442
|
+
# Configure item expiration
|
|
443
|
+
#
|
|
444
|
+
# @return [void]
|
|
445
|
+
#
|
|
446
|
+
# @example Get the current expire setting
|
|
447
|
+
# expire
|
|
448
|
+
# @example Clear any expire setting
|
|
449
|
+
# expire nil
|
|
450
|
+
# @example Use a duration
|
|
451
|
+
# expire 5.hours
|
|
452
|
+
# @example Use a string duration
|
|
453
|
+
# expire "5h"
|
|
454
|
+
# @example Set a specific state on expiration
|
|
455
|
+
# expire 5.minutes, NULL
|
|
456
|
+
# expire 5.minutes, state: NULL
|
|
457
|
+
# @example Send a command on expiration
|
|
458
|
+
# expire 5.minutes, command: OFF
|
|
459
|
+
def expire(*args, command: nil, state: nil)
|
|
460
|
+
unless (0..2).cover?(args.length)
|
|
461
|
+
raise ArgumentError,
|
|
462
|
+
"wrong number of arguments (given #{args.length}, expected 0..2)"
|
|
463
|
+
end
|
|
464
|
+
return @expire if args.empty?
|
|
465
|
+
|
|
466
|
+
state = args.last if args.length == 2
|
|
467
|
+
raise ArgumentError, "cannot provide both command and state" if command && state
|
|
468
|
+
|
|
469
|
+
duration = args.first
|
|
470
|
+
return @expire = nil if duration.nil?
|
|
471
|
+
|
|
472
|
+
duration = duration.to_s[2..].downcase if duration.is_a?(Duration)
|
|
473
|
+
state = "'#{state}'" if state.respond_to?(:to_str) && type == :string
|
|
474
|
+
@expire = duration
|
|
475
|
+
@expire += ",state=#{state}" if state
|
|
476
|
+
@expire += ",command=#{command}" if command
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# @!visibility private
|
|
480
|
+
def build
|
|
481
|
+
item = create_item
|
|
482
|
+
item.label = label
|
|
483
|
+
item.category = icon.to_s if icon
|
|
484
|
+
groups.each do |group|
|
|
485
|
+
group = group.name if group.respond_to?(:name)
|
|
486
|
+
item.add_group_name(group.to_s)
|
|
487
|
+
end
|
|
488
|
+
tags.each do |tag|
|
|
489
|
+
item.add_tag(tag)
|
|
490
|
+
end
|
|
491
|
+
item.metadata.merge!(metadata)
|
|
492
|
+
item.metadata["autoupdate"] = autoupdate.to_s unless autoupdate.nil?
|
|
493
|
+
item.metadata["expire"] = expire if expire
|
|
494
|
+
item.metadata["stateDescription"] = { "pattern" => format } if format
|
|
495
|
+
item.state = item.format_update(state) unless state.nil?
|
|
496
|
+
item
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# @return [String]
|
|
500
|
+
def inspect
|
|
501
|
+
s = "#<OpenHAB::Core::Items::#{inspect_type}ItemBuilder#{type_details} #{name} #{label.inspect}"
|
|
502
|
+
s += " category=#{icon.inspect}" if icon
|
|
503
|
+
s += " tags=#{tags.inspect}" unless tags.empty?
|
|
504
|
+
s += " groups=#{groups.map { |g| g.respond_to?(:name) ? g.name : g }.inspect}" unless groups.empty?
|
|
505
|
+
s += " metadata=#{metadata.to_h.inspect}" unless metadata.empty?
|
|
506
|
+
"#{s}>"
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
private
|
|
510
|
+
|
|
511
|
+
# @return [String]
|
|
512
|
+
def inspect_type
|
|
513
|
+
type.to_s.capitalize
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
# @return [String, nil]
|
|
517
|
+
def type_details
|
|
518
|
+
":#{dimension}" if dimension
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def create_item
|
|
522
|
+
type = @type.to_s.gsub(/(?:^|_)[a-z]/) { |match| match[-1].upcase }
|
|
523
|
+
type = "#{type}:#{dimension}" if dimension
|
|
524
|
+
self.class.item_factory.create_item(type, name)
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
# Allows customizing a group. You can also call any method from {Builder}, and those
|
|
529
|
+
# items will automatically be a member of this group.
|
|
530
|
+
class GroupItemBuilder < ItemBuilder
|
|
531
|
+
include Builder
|
|
532
|
+
|
|
533
|
+
Builder.public_instance_methods.each do |m|
|
|
534
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
535
|
+
def #{m}(*args, groups: nil, **kwargs) # def dimmer_item(*args, groups: nil, **kwargs)
|
|
536
|
+
groups ||= [] # groups ||= []
|
|
537
|
+
groups << self # groups << self
|
|
538
|
+
super # super
|
|
539
|
+
end # end
|
|
540
|
+
RUBY
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
FUNCTION_REGEX = /^([a-z]+)(?:\(([a-z]+)(?:,([a-z]+))*\))?/i.freeze
|
|
544
|
+
private_constant :FUNCTION_REGEX
|
|
545
|
+
|
|
546
|
+
# The combiner function for this group
|
|
547
|
+
# @return [String, nil]
|
|
548
|
+
attr_accessor :function
|
|
549
|
+
# A thing to be used as the base for the channel of any member items
|
|
550
|
+
# @return [Core::Things::ThingUID, Core::Things::Thing, String, nil]
|
|
551
|
+
attr_accessor :thing
|
|
552
|
+
# A prefix to be added to the name of any member items
|
|
553
|
+
# @return [String, nil]
|
|
554
|
+
attr_accessor :name_base
|
|
555
|
+
# A prefix to be added to the label of any member items
|
|
556
|
+
# @return [String, nil]
|
|
557
|
+
attr_accessor :label_base
|
|
558
|
+
# Members to be created in this group
|
|
559
|
+
# @return [Array<ItemBuilder>]
|
|
560
|
+
attr_reader :members
|
|
561
|
+
|
|
562
|
+
# @param type [Symbol, nil] The base type for the group
|
|
563
|
+
# @param function [String, nil] The combiner function for this group
|
|
564
|
+
# @param thing [Core::Things::ThingUID, Core::Things::Thing, String, nil]
|
|
565
|
+
# A Thing to be used as the base for the channel for any contained items.
|
|
566
|
+
# @param (see ItemBuilder#initialize)
|
|
567
|
+
def initialize(*args, type: nil, function: nil, thing: nil, **kwargs)
|
|
568
|
+
raise ArgumentError, "invalid function #{function}" if function && !function.match?(FUNCTION_REGEX)
|
|
569
|
+
raise ArgumentError, "state cannot be set on GroupItems" if kwargs[:state]
|
|
570
|
+
|
|
571
|
+
super(type, *args, **kwargs)
|
|
572
|
+
@function = function
|
|
573
|
+
@members = []
|
|
574
|
+
@thing = thing
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# @!visibility private
|
|
578
|
+
def create_item
|
|
579
|
+
base_item = super if type
|
|
580
|
+
if function
|
|
581
|
+
match = function.match(FUNCTION_REGEX)
|
|
582
|
+
|
|
583
|
+
dto = org.openhab.core.items.dto.GroupFunctionDTO.new
|
|
584
|
+
dto.name = match[1]
|
|
585
|
+
dto.params = match[2..]
|
|
586
|
+
function = org.openhab.core.items.dto.ItemDTOMapper.map_function(base_item, dto)
|
|
587
|
+
Core::Items::GroupItem.new(name, base_item, function)
|
|
588
|
+
else
|
|
589
|
+
Core::Items::GroupItem.new(name, base_item)
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
# @!visibility private
|
|
594
|
+
def add(child_item)
|
|
595
|
+
@members << child_item
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
private
|
|
599
|
+
|
|
600
|
+
# @return [String]
|
|
601
|
+
def inspect_type
|
|
602
|
+
"Group"
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# @return [String, nil]
|
|
606
|
+
def type_details
|
|
607
|
+
r = super
|
|
608
|
+
r = "#{r}:#{function}" if function
|
|
609
|
+
r
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
def provider
|
|
613
|
+
self
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenHAB
|
|
4
|
+
module DSL
|
|
5
|
+
module Items
|
|
6
|
+
# Functionality to implement `ensure`/`ensure_states`
|
|
7
|
+
module Ensure
|
|
8
|
+
# Contains the `ensure` method mixed into {GenericItem} and {GroupItem::Members}
|
|
9
|
+
module Ensurable
|
|
10
|
+
# Fluent method call that you can chain commands on to, that will
|
|
11
|
+
# then automatically ensure that the item is not in the command's
|
|
12
|
+
# state before sending the command.
|
|
13
|
+
#
|
|
14
|
+
# @example Turn switch on only if it's not on
|
|
15
|
+
# MySwitch.ensure.on
|
|
16
|
+
# @example Turn on all switches in a group that aren't already on
|
|
17
|
+
# MySwitchGroup.members.ensure.on
|
|
18
|
+
def ensure
|
|
19
|
+
GenericItemDelegate.new(self)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Extensions for {::GenericItem} to implement {Ensure}'s functionality
|
|
24
|
+
module GenericItem
|
|
25
|
+
include Ensurable
|
|
26
|
+
|
|
27
|
+
Core::Items::GenericItem.prepend(self)
|
|
28
|
+
|
|
29
|
+
# If `ensure_states` is active (by block or chained method), then
|
|
30
|
+
# check if this item is in the command's state before actually
|
|
31
|
+
# sending the command
|
|
32
|
+
%i[command update].each do |ensured_method|
|
|
33
|
+
# def command(state)
|
|
34
|
+
# return super(state) unless Thread.current[:openhab_ensure_states]
|
|
35
|
+
#
|
|
36
|
+
# logger.trace do
|
|
37
|
+
# "#{name} ensure #{state}, format_command: #{format_command(state)}, current state: #{self.state}"
|
|
38
|
+
# end
|
|
39
|
+
# return if self.state == format_command(state)
|
|
40
|
+
#
|
|
41
|
+
# super(state)
|
|
42
|
+
# end
|
|
43
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
|
|
44
|
+
def #{ensured_method}(state)
|
|
45
|
+
return super(state) unless Thread.current[:openhab_ensure_states]
|
|
46
|
+
|
|
47
|
+
logger.trace do
|
|
48
|
+
"\#{name} ensure \#{state}, format_#{ensured_method}: \#{format_#{ensured_method}(state)}, current state: \#{self.state}"
|
|
49
|
+
end
|
|
50
|
+
return if self.state == format_#{ensured_method}(state)
|
|
51
|
+
|
|
52
|
+
super(state)
|
|
53
|
+
end
|
|
54
|
+
RUBY
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# "anonymous" class that wraps any method call in `ensure_states`
|
|
59
|
+
# before forwarding to the wrapped object
|
|
60
|
+
# @!visibility private
|
|
61
|
+
class GenericItemDelegate
|
|
62
|
+
def initialize(item)
|
|
63
|
+
@item = item
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @!visibility private
|
|
67
|
+
# this is explicitly defined, instead of aliased, because #command
|
|
68
|
+
# doesn't actually exist as a method, and will go through method_missing
|
|
69
|
+
def <<(command)
|
|
70
|
+
command(command)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# activate `ensure_states` before forwarding to the wrapped object
|
|
74
|
+
def method_missing(method, *args, &block)
|
|
75
|
+
return super unless @item.respond_to?(method)
|
|
76
|
+
|
|
77
|
+
DSL.ensure_states do
|
|
78
|
+
@item.__send__(method, *args, &block)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
ruby2_keywords :method_missing if respond_to? :ruby2_keywords
|
|
82
|
+
|
|
83
|
+
# .
|
|
84
|
+
def respond_to_missing?(method, include_private = false)
|
|
85
|
+
@item.respond_to?(method, include_private) || super
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
Core::Items::GroupItem::Members.include(Ensure::Ensurable)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|