openhab-scripting 5.33.1 → 5.34.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96a5a45996611268693311513fde61baca47e46134e1184ac6556b29a567689f
4
- data.tar.gz: 8a70e85d3d9b892cca7b3ee9b912751a9c702ff784dc4827ecc16ef816d58c5c
3
+ metadata.gz: 9300c92dc3d585e27528126b0a04b3b6f54a3af5e48ebbdd967b52b6b8d3efe8
4
+ data.tar.gz: 4f0a4331c7cd29d49c3013cee2033536ec5095471a82bd3b6e4fe6acfbf4fa8d
5
5
  SHA512:
6
- metadata.gz: 04b505a18d90437f38ed06005efa1815601a67b471da37814f8284e62aa6f92e6ac7955c726f4c1455f210c5fe0afcb350ae0ddc23722adec2906be307741ec7
7
- data.tar.gz: dd080606344af5b3e9eea7310a7dad12b38fa18dbd2d028e04c46275d316c60ee0e5a44a23b474120fced82eeee655b0e429e336027a3d49a115e779e94e0c0e
6
+ metadata.gz: bbd50fbc7ae916736accef799f512a2213995ed839c28117ad96eb3be9ac79344df192ad7d5e131deb58b2a49fdc1124f146f711aa1c784099b5947e62465e6d
7
+ data.tar.gz: 9610a22c54a861856349bbaf77c7a450f7905e3be390aa9a12389d30f17e07ba708fd8516079c89dfd13e79a76e2d99630e3da43bc9bc1dcbf98b7de942d0103
@@ -199,7 +199,10 @@ module OpenHAB
199
199
  end
200
200
 
201
201
  # Delegate missing methods to {base_item} if possible
202
- def method_missing(method, *args, &block)
202
+ # Command methods and predicate methods for GroupItem are performed here
203
+ # instead of being statically defined in items.rb
204
+ # because base_item is needed to determine the command/data types but is not available in the static context
205
+ ruby2_keywords def method_missing(method, *args, &block)
203
206
  return base_item.__send__(method, *args, &block) if base_item&.respond_to?(method) # rubocop:disable Lint/RedundantSafeNavigation nil responds to :to_a
204
207
 
205
208
  super
@@ -50,9 +50,7 @@ module OpenHAB
50
50
  # @return [self]
51
51
  #
52
52
  def toggle(source: nil)
53
- return on!(source: source) unless state?
54
-
55
- command!(!state, source: source)
53
+ command!(!on?, source: source)
56
54
  end
57
55
 
58
56
  # @!method on?
@@ -74,13 +74,17 @@ module OpenHAB
74
74
  RUBY
75
75
 
76
76
  Enumerable.class_eval <<~RUBY, __FILE__, __LINE__ + 1
77
- def #{command} # def on
78
- each(&:#{command}) # each(&:on)
79
- end # end
80
- #
81
- def #{command}! # def on!
82
- each(&:#{command}!) # each(&:on!)
83
- end # end
77
+ ruby2_keywords def #{command}(*args, &block) # ruby2_keywords def on(*args, &block)
78
+ each do |member| # each do |member|
79
+ member.#{command}(*args, &block) # member.on(*args, &block)
80
+ end # end
81
+ end # end
82
+ #
83
+ ruby2_keywords def #{command}!(*args, &block) # ruby2_keywords def on!(*args, &block)
84
+ each do |member| # each do |member|
85
+ member.#{command}!(*args, &block) # member.on!(*args, &block)
86
+ end # end
87
+ end # end
84
88
  RUBY
85
89
  else
86
90
  logger.trace { "Defining #{klass}/Enumerable##{command} for #{value}" }
@@ -142,7 +142,7 @@ module OpenHAB
142
142
  end
143
143
 
144
144
  # @!visibility private
145
- def register(id, block, label: nil, config_description: nil)
145
+ def register(id, block, label: nil, type: :state, config_description: nil)
146
146
  uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
147
147
  uri = java.net.URI.new("profile", uid.to_s, nil)
148
148
  if config_description && config_description.uid != uri
@@ -155,6 +155,7 @@ module OpenHAB
155
155
  @profiles[uid] = {
156
156
  thread_locals: DSL::ThreadLocal.persist,
157
157
  label: label,
158
+ type: type,
158
159
  config_description: config_description,
159
160
  block: block
160
161
  }
@@ -177,7 +178,11 @@ module OpenHAB
177
178
  @profiles.map do |uid, profile|
178
179
  next if profile[:label].nil?
179
180
 
180
- org.openhab.core.thing.profiles.ProfileTypeBuilder.new_state(uid, "RUBY #{profile[:label]}").build
181
+ if profile[:type] == :trigger
182
+ org.openhab.core.thing.profiles.ProfileTypeBuilder.new_trigger(uid, "RUBY #{profile[:label]}").build
183
+ else
184
+ org.openhab.core.thing.profiles.ProfileTypeBuilder.new_state(uid, "RUBY #{profile[:label]}").build
185
+ end
181
186
  end.compact
182
187
  end
183
188
 
@@ -43,14 +43,22 @@ module OpenHAB
43
43
  end
44
44
  end
45
45
 
46
- # @param [ZonedDateTime, nil] context
47
- # A {ZonedDateTime} used to fill in missing
48
- # fields during conversion. Not used in this class.
49
- # @return [ZonedTimeTime]
50
- def to_zoned_date_time(context = nil) # rubocop:disable Lint/UnusedMethodArgument
51
- zoned_date_time
46
+ # @deprecated OH 4.2 Just call zoned_date_time(ZoneId.system_default) in OH 4.3
47
+ if OpenHAB::Core.version >= OpenHAB::Core::V4_3
48
+ def to_zoned_date_time(context = nil) # rubocop:disable Lint/UnusedMethodArgument
49
+ zoned_date_time(ZoneId.system_default)
50
+ end
51
+ else
52
+ def to_zoned_date_time(context = nil) # rubocop:disable Lint/UnusedMethodArgument
53
+ zoned_date_time
54
+ end
52
55
  end
53
56
 
57
+ # @!method to_zoned_date_time(context = nil)
58
+ # @param [ZonedDateTime, nil] context
59
+ # A {ZonedDateTime} used to fill in missing fields during conversion. Not used in this class.
60
+ # @return [ZonedDateTime]
61
+
54
62
  # @!visibility private
55
63
  def to_instant(_context = nil)
56
64
  # @deprecated OH 3.4 getInstant() was added in OH 4.0
@@ -62,6 +70,7 @@ module OpenHAB
62
70
  # @!method to_instant
63
71
  # @return [Instant]
64
72
 
73
+ # @deprecated These methods have been deprecated in openHAB 4.3.
65
74
  # act like a Ruby Time
66
75
  def_delegator :zoned_date_time, :month_value, :month
67
76
  def_delegator :zoned_date_time, :day_of_month, :mday
@@ -69,9 +78,14 @@ module OpenHAB
69
78
  def_delegator :zoned_date_time, :minute, :min
70
79
  def_delegator :zoned_date_time, :second, :sec
71
80
  def_delegator :zoned_date_time, :nano, :nsec
72
- def_delegator :zoned_date_time, :to_epoch_second, :to_i
73
81
  def_delegator :zoned_date_time, :to_time
74
82
 
83
+ # NOTE: to_i is supported by both Instant and ZonedDateTime in #method_missing
84
+
85
+ # @!method to_i
86
+ # Returns the value of time as an integer number of seconds since the Epoch
87
+ # @return [Integer] Number of seconds since the Epoch
88
+
75
89
  # @!visibility private
76
90
  alias_method :day, :mday
77
91
 
@@ -84,6 +98,9 @@ module OpenHAB
84
98
  if value.nil?
85
99
  super()
86
100
  return
101
+ elsif OpenHAB::Core.version >= OpenHAB::Core::V4_3 && value.respond_to?(:to_instant)
102
+ super(value.to_instant)
103
+ return
87
104
  elsif value.respond_to?(:to_zoned_date_time)
88
105
  super(value.to_zoned_date_time)
89
106
  return
@@ -110,6 +127,9 @@ module OpenHAB
110
127
  def eql?(other)
111
128
  return false unless other.instance_of?(self.class)
112
129
 
130
+ # @deprecated OH 4.2 Call compare_to(other).zero? in OH 4.3 to avoid the deprecated getZonedDateTime()
131
+ return compare_to(other).zero? if OpenHAB::Core.version >= OpenHAB::Core::V4_3
132
+
113
133
  zoned_date_time.compare_to(other.zoned_date_time).zero?
114
134
  end
115
135
 
@@ -126,6 +146,9 @@ module OpenHAB
126
146
  def <=>(other)
127
147
  logger.trace { "(#{self.class}) #{self} <=> #{other} (#{other.class})" }
128
148
  if other.is_a?(self.class)
149
+ # @deprecated OH 4.2 Call compare_to(other) in OH 4.3 to avoid the deprecated getZonedDateTime()
150
+ return compare_to(other) if OpenHAB::Core.version >= OpenHAB::Core::V4_3
151
+
129
152
  zoned_date_time <=> other.zoned_date_time
130
153
  elsif other.respond_to?(:to_time)
131
154
  to_time <=> other.to_time
@@ -147,6 +170,7 @@ module OpenHAB
147
170
  #
148
171
  def coerce(other)
149
172
  logger.trace { "Coercing #{self} as a request from #{other.class}" }
173
+ return [other, to_instant] if other.respond_to?(:to_instant)
150
174
  return [other, zoned_date_time] if other.respond_to?(:to_zoned_date_time)
151
175
 
152
176
  [DateTimeType.new(other), self] if other.respond_to?(:to_time)
@@ -158,7 +182,7 @@ module OpenHAB
158
182
  # @return [Float] Number of seconds since the Epoch, with nanosecond presicion
159
183
  #
160
184
  def to_f
161
- zoned_date_time.to_epoch_second + (zoned_date_time.nano / 1_000_000_000)
185
+ to_instant.then { |instant| instant.epoch_second + (instant.nano / 1_000_000_000) }
162
186
  end
163
187
 
164
188
  #
@@ -166,6 +190,7 @@ module OpenHAB
166
190
  #
167
191
  # @return [Integer] The offset from UTC, in seconds
168
192
  #
193
+ # @deprecated This method has been deprecated in openHAB 4.3.
169
194
  def utc_offset
170
195
  zoned_date_time.offset.total_seconds
171
196
  end
@@ -175,6 +200,7 @@ module OpenHAB
175
200
  #
176
201
  # @return [true,false] true if utc_offset == 0, false otherwise
177
202
  #
203
+ # @deprecated This method has been deprecated in openHAB 4.3.
178
204
  def utc?
179
205
  utc_offset.zero?
180
206
  end
@@ -184,6 +210,7 @@ module OpenHAB
184
210
  #
185
211
  # @return [Integer] The day of week
186
212
  #
213
+ # @deprecated This method has been deprecated in openHAB 4.3.
187
214
  def wday
188
215
  zoned_date_time.day_of_week.value % 7
189
216
  end
@@ -200,6 +227,8 @@ module OpenHAB
200
227
 
201
228
  # @!visibility private
202
229
  def respond_to_missing?(method, _include_private = false)
230
+ # @deprecated OH 4.2 Remove version check when dropping OH 4.2
231
+ return true if OpenHAB::Core.version >= OpenHAB::Core::V4_3 && to_instant.respond_to?(method)
203
232
  return true if zoned_date_time.respond_to?(method)
204
233
  return true if ::Time.instance_methods.include?(method.to_sym)
205
234
 
@@ -211,6 +240,11 @@ module OpenHAB
211
240
  # object representing the same instant
212
241
  #
213
242
  def method_missing(method, *args, &block)
243
+ # @deprecated OH 4.2 Remove version check when dropping OH 4.2
244
+ if OpenHAB::Core.version >= OpenHAB::Core::V4_3 && to_instant.respond_to?(method)
245
+ return to_instant.send(method, *args, &block)
246
+ end
247
+
214
248
  return zoned_date_time.send(method, *args, &block) if zoned_date_time.respond_to?(method)
215
249
  return to_time.send(method, *args, &block) if ::Time.instance_methods.include?(method.to_sym)
216
250
 
@@ -98,8 +98,6 @@ module OpenHAB
98
98
  result
99
99
  end
100
100
 
101
- include DSL
102
-
103
101
  private
104
102
 
105
103
  def item(*args, **kwargs, &block)
@@ -159,10 +157,11 @@ module OpenHAB
159
157
 
160
158
  # make sure to add the item to the registry before linking it
161
159
  channel_uids = builder.channels.to_set do |(channel, config)|
162
- # fill in partial channel names from group's thing id
160
+ channel = channel.to_s
161
+ # fill in partial channel names from item's or group's thing id
163
162
  if !channel.include?(":") &&
164
- (group = builder.groups.find { |g| g.is_a?(GroupItemBuilder) && g.thing })
165
- thing = group.thing
163
+ (thing = builder.thing ||
164
+ thing = builder.groups.find { |g| g.is_a?(GroupItemBuilder) && g.thing }&.thing)
166
165
  channel = "#{thing}:#{channel}"
167
166
  end
168
167
 
@@ -243,18 +242,12 @@ module OpenHAB
243
242
  # The icon to be associated with the item
244
243
  # @return [Symbol, String, nil]
245
244
  attr_accessor :icon
246
- # Groups to which this item should be added
247
- # @return [Array<String, GroupItem>]
248
- attr_reader :groups
249
- # Tags to apply to this item
250
- # @return [Array<String, Semantics::Tag>]
251
- attr_reader :tags
252
245
  # Autoupdate setting
253
246
  # @return [true, false, nil]
254
247
  attr_accessor :autoupdate
255
- # {Core::Things::ChannelUID Channel} to link the item to
256
- # @return [String, Core::Things::ChannelUID, nil]
257
- attr_accessor :channels
248
+ # @return [String, Core::Things::Thing, Core::Things::ThingUID, nil]
249
+ # {Core::Things::ThingUID Thing} from which to resolve relative channel ids
250
+ attr_accessor :thing
258
251
  # @return [Core::Items::Metadata::NamespaceHash]
259
252
  attr_reader :metadata
260
253
  # Initial state
@@ -264,6 +257,19 @@ module OpenHAB
264
257
  # @return [Core::Types::State]
265
258
  attr_reader :state
266
259
 
260
+ attr_writer :channels, :groups, :tags
261
+
262
+ # @!attribute [rw] channels
263
+ # @return [Array<String, Symbol, Core::Things::ChannelUID, Core::Things::Channel, Array>]
264
+ # {Core::Things::ChannelUID Channel} to link the item to
265
+
266
+ # @!attribute [rw] groups
267
+ # @return [Array<String, GroupItem>] Groups to which this item should be added
268
+
269
+ # @!attribute [rw] tags
270
+ # @return [Array<String>] Tags to apply to this item
271
+
272
+ # This comment needs to exist otherwise YARD thinks the above attribute should apply to the metaclass
267
273
  class << self
268
274
  # @!visibility private
269
275
  def item_factory
@@ -352,6 +358,7 @@ module OpenHAB
352
358
  autoupdate: nil,
353
359
  thing: nil,
354
360
  channel: nil,
361
+ channels: nil,
355
362
  expire: nil,
356
363
  alexa: nil,
357
364
  ga: nil, # rubocop:disable Naming/MethodParameterName
@@ -408,12 +415,13 @@ module OpenHAB
408
415
  self.state = state
409
416
 
410
417
  self.group(*group)
411
- self.group(*groups)
418
+ self.groups(*groups)
412
419
 
413
420
  self.tag(*tag)
414
- self.tag(*tags)
421
+ self.tags(*tags)
415
422
 
416
- self.channel(*channel) if channel
423
+ self.channel(*channel)
424
+ self.channels(*channels)
417
425
  end
418
426
 
419
427
  #
@@ -428,20 +436,45 @@ module OpenHAB
428
436
  #
429
437
  # Tag item
430
438
  #
431
- # @param tags [String, Symbol, Semantics::Tag]
432
- # @return [void]
439
+ # @return [Array<String>]
440
+ #
441
+ # @overload tag(tag)
442
+ # @param tag [String, Symbol, Semantics::Tag]
443
+ # @return [Array<String>]
444
+ #
445
+ # @overload tags
446
+ # @return [Array<String>]
447
+ #
448
+ # @overload tags(*tags)
449
+ # @param tags [String, Symbol, Semantics::Tag]
450
+ # @return [Array<String>]
433
451
  #
434
452
  def tag(*tags)
435
- @tags += self.class.normalize_tags(*tags)
453
+ return @tags if tags.empty?
454
+
455
+ @tags.concat(self.class.normalize_tags(*tags))
436
456
  end
457
+ alias_method :tags, :tag
437
458
 
438
459
  #
439
460
  # Add this item to a group
440
461
  #
441
- # @param groups [String, GroupItemBuilder, GroupItem]
442
- # @return [void]
462
+ # @return [Array<String, GroupItemBulder, GroupItem>]
463
+ #
464
+ # @overload group(group)
465
+ # @param group [String, GroupItemBuilder, GroupItem]
466
+ # @return [Array<String, GroupItemBulder, GroupItem>]
467
+ #
468
+ # @overload groups
469
+ # @return [Array<String, GroupItemBulder, GroupItem>]
470
+ #
471
+ # @overload groups(*groups)
472
+ # @param groups [String, GroupItemBuilder, GroupItem]
473
+ # @return [Array<String, GroupItemBulder, GroupItem>]
443
474
  #
444
475
  def group(*groups)
476
+ return @groups if groups.empty?
477
+
445
478
  unless groups.all? do |group|
446
479
  group.is_a?(String) || group.is_a?(Core::Items::GroupItem) || group.is_a?(GroupItemBuilder)
447
480
  end
@@ -450,6 +483,7 @@ module OpenHAB
450
483
 
451
484
  @groups.concat(groups)
452
485
  end
486
+ alias_method :groups, :group
453
487
 
454
488
  #
455
489
  # @!method alexa(value, config = nil)
@@ -494,10 +528,23 @@ module OpenHAB
494
528
  #
495
529
  # Add a channel link to this item.
496
530
  #
497
- # @param channel [String, Symbol, Core::Things::ChannelUID, Core::Things::Channel]
498
- # Channel to link the item to. When thing is set, this can be a relative channel name.
499
- # @param config [Hash] Additional configuration, such as profile
500
- # @return [void]
531
+ # @return [Array<String, Symbol, Core::Things::ChannelUID, Core::Things::Channel, Array>]
532
+ #
533
+ # @overload channel(channel, config = {})
534
+ # @param channel [String, Symbol, Core::Things::ChannelUID, Core::Things::Channel]
535
+ # Channel to link the item to. When {thing} is set, this can be a relative channel name.
536
+ # @param config [Hash] Additional configuration, such as profile
537
+ # @return [Array<String, Symbol, Core::Things::ChannelUID, Core::Things::Channel, Array>]
538
+ #
539
+ # @overload channels
540
+ # @return [Array<String, Symbol, Core::Things::ChannelUID, Core::Things::Channel, Array>]
541
+ #
542
+ # @overload channels(*channels)
543
+ # @param channels [String, Symbol, Core::Things::ChannelUID, Core::Things::Channel, Array]
544
+ # Channels to link the item to. When {thing} is set, these can be relative channel names.
545
+ # Each array element can also be a two element array with the first element being the
546
+ # channel, and the second element being a config hash.
547
+ # @return [Array<String, Symbol, Core::Things::ChannelUID, Core::Things::Channel, Array>]
501
548
  #
502
549
  # @example
503
550
  # items.build do
@@ -511,11 +558,46 @@ module OpenHAB
511
558
  # switch_item Bedroom_Light, thing: "mqtt:topic:bedroom-light", channel: :power
512
559
  # end
513
560
  #
514
- def channel(channel, config = {})
515
- channel = channel.to_s
516
- channel = "#{@thing}:#{channel}" if @thing && !channel.include?(":")
517
- @channels << [channel, config]
561
+ # @example Multiple channels
562
+ # items.build do
563
+ # dimmer_item DemoDimmer, channels: ["hue:0210:bridge:1:color", "knx:device:bridge:generic:controlDimmer"]
564
+ # end
565
+ #
566
+ # @example Multiple channels in a block
567
+ # items.build do
568
+ # dimmer_item DemoDimmer do
569
+ # channel "hue:0210:bridge:1:color"
570
+ # channel "knx:device:bridge:generic:controlDimmer"
571
+ # end
572
+ # end
573
+ #
574
+ # @example Multiple channels with config
575
+ # items.build do
576
+ # dimmer_item DemoDimmer, channels: [["hue:0210:bridge:1:color", profile: "system:follow"],
577
+ # "knx:device:bridge:generic:controlDimmer"]
578
+ # end
579
+ #
580
+ def channel(*channels)
581
+ return @channels if channels.empty?
582
+
583
+ channels = [channels] if channels.length == 2 && channels[1].is_a?(Hash)
584
+
585
+ channels.each do |channel|
586
+ orig_channel = channel
587
+ channel = channel.first if channel.is_a?(Array)
588
+ next if channel.is_a?(String) ||
589
+ channel.is_a?(Symbol) ||
590
+ channel.is_a?(Core::Things::ChannelUID) ||
591
+ channel.is_a?(Core::Things::Channel)
592
+
593
+ raise ArgumentError, "channel #{orig_channel.inspect} must be a `String`, `Symbol`, `ChannelUID`, or " \
594
+ "`Channel`, or a two element array with the first element those types, and the " \
595
+ "second element a Hash"
596
+ end
597
+
598
+ @channels.concat(channels)
518
599
  end
600
+ alias_method :channels, :channel
519
601
 
520
602
  #
521
603
  # @!method expire(duration, command: nil, state: nil, ignore_state_updates: nil, ignore_commands: nil)
@@ -4,6 +4,6 @@ module OpenHAB
4
4
  module DSL
5
5
  # Version of openHAB helper libraries
6
6
  # @return [String]
7
- VERSION = "5.33.1"
7
+ VERSION = "5.34.0"
8
8
  end
9
9
  end
data/lib/openhab/dsl.rb CHANGED
@@ -106,6 +106,7 @@ module OpenHAB
106
106
  #
107
107
  # @param [String, Symbol] id The id for the profile.
108
108
  # @param [String, nil] label The label for the profile. When nil, the profile will not be visible in the UI.
109
+ # @param [:state, :trigger] type The type of profile.
109
110
  # @param [org.openhab.core.config.core.ConfigDescription, nil] config_description
110
111
  # The configuration description for the profile so that it can be configured in the UI.
111
112
  # @yield [event, command: nil, state: nil, trigger: nil, time_series: nil, callback:, link:, item:, channel_uid:, configuration:, context:]
@@ -191,13 +192,13 @@ module OpenHAB
191
192
  # (configuration["min"]..configuration["max"]).cover?(state)
192
193
  # end
193
194
  #
194
- def profile(id, label: nil, config_description: nil, &block)
195
+ def profile(id, label: nil, type: :state, config_description: nil, &block)
195
196
  raise ArgumentError, "Block is required" unless block
196
197
 
197
198
  id = id.to_s
198
199
 
199
200
  ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
200
- Core::ProfileFactory.instance.register(id, block, label: label, config_description: config_description)
201
+ Core::ProfileFactory.instance.register(id, block, label: label, type: type, config_description: config_description)
201
202
  end
202
203
  end
203
204
 
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.33.1
4
+ version: 5.34.0
5
5
  platform: ruby
6
- original_platform: ''
7
6
  authors:
8
7
  - Brian O'Connell
9
8
  - Cody Cutrer
10
9
  - Jimmy Tanagra
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2024-12-16 00:00:00.000000000 Z
12
+ date: 2025-01-03 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: bundler
@@ -488,7 +487,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
488
487
  - !ruby/object:Gem::Version
489
488
  version: '0'
490
489
  requirements: []
491
- rubygems_version: 3.6.0
490
+ rubygems_version: 3.6.2
492
491
  specification_version: 4
493
492
  summary: JRuby Helper Libraries for openHAB Scripting
494
493
  test_files: []