Module: OpenHAB::Core::Items::Semantics

Included in:
GenericItem
Defined in:
lib/openhab/core/items/semantics.rb,
lib/openhab/core/items/semantics/provider.rb,
lib/openhab/core/items/semantics/semantic_tag.rb,
lib/openhab/core/items/semantics/tag_class_methods.rb

Overview

Module for implementing semantics helper methods on Item in order to easily navigate the Semantic Model in your scripts. This can be extremely useful to find related items in rules that are executed for any member of a group.

Wraps orgorg.openhaborg.openhab.coreorg.openhab.core.modelorg.openhab.core.model.scriptorg.openhab.core.model.script.actionsorg.openhab.core.model.script.actions.Semantics as well as adding a few additional convenience methods. Also includes classes for each semantic tag.

Be warned that the Semantic model is stricter than can actually be described by tags and groups on an Item. It makes assumptions that any given item only belongs to one semantic type (Location, Equipment, Point).

## Enumerable helper methods

Enumerable helper methods are also provided to complement the semantic model. These methods can be chained together to find specific item(s) based on custom tags or group memberships that are outside the semantic model.

The Enumerable helper methods apply to:

## Semantic Classes

Each [Semantic Tag](github.com/openhab/openhab-core/blob/main/bundles/org.openhab.core.semantics/model/SemanticTags.csv) has a corresponding class within the orgorg.openhaborg.openhab.coreorg.openhab.core.semantics class hierarchy. These “semantic classes” are available as constants in the Semantics module with the corresponding name. The following table illustrates the semantic constants:

| Semantic Constant | openHAB’s Semantic Class | | ———————– | —————————————————— | | ‘Semantics::LivingRoom` | `org.openhab.core.semantics.model.location.LivingRoom` | | `Semantics::Lightbulb` | `org.openhab.core.semantics.model.equipment.Lightbulb` | | `Semantics::Control` | `org.openhab.core.semantics.model.point.Control` | | `Semantics::Switch` | `org.openhab.core.semantics.model.point.Switch` | | `Semantics::Power` | `org.openhab.core.semantics.model.property.Power` | | … | … |

These constants can be used as arguments to the #points, Enumerable#locations and Enumerable#equipments methods to filter their results. They can also be compared against the return value of #semantic_type, #location_type, #equipment_type, #point_type, and #property_type. They can even be used with DSL::Items::ItemBuilder#tag.

In openHAB 4.0, all of the tag objects implement SemanticTag.

In openHAB 3.4, the constants in the Semantics module are enhanced with TagClassMethods to provide easy access to the tags’ additional attributes: label, synonyms, and description. For example, to get the synonyms for ‘Semantics::Lightbulb` in German: `Semantics::Lightbulb.synonyms(java.util.Locale::GERMAN)`

## Adding Custom Semantic Tags

openHAB 4.0 supports adding custom semantic tags to augment the standard set of tags to better suit your particular requirements.

For more information, see Semantics.add

Examples:

Working with tags

# Return an array of sibling points with a "Switch" tag
Light_Color.points(Semantics::Switch)

# check semantic type
LoungeRoom_Light.equipment_type == Semantics::Lightbulb
Light_Color.property_type == Semantics::Light

switches.items

Group   gFullOn
Group   gRoomOff

Group   eGarageLights        "Garage Lights"             (lGarage)                 [ "Lightbulb" ]
Dimmer  GarageLights_Dimmer  "Garage Lights"    <light>  (eGarageLights)           [ "Switch" ]
Number  GarageLights_Scene   "Scene"                     (eGarageLights, gFullOn, gRoomOff)

Group   eMudLights           "Mud Room Lights"           (lMud)                    [ "Lightbulb" ]
Dimmer  MudLights_Dimmer     "Garage Lights"    <light>  (eMudLights)              [ "Switch" ]
Number  MudLights_Scene      "Scene"                     (eMudLights, gFullOn, gRoomOff)

Find the switch item for a scene channel on a zwave dimmer

rule "turn dimmer to full on when switch double-tapped up" do
  changed gFullOn.members, to: 1.3
  run do |event|
    dimmer_item = event.item.points(Semantics::Switch).first
    dimmer_item.ensure << 100
  end
end

Turn off all the lights in a room

rule "turn off all lights in the room when switch double-tapped down" do
  changed gRoomOff.members, to: 2.3
  run do |event|
    event
      .item
      .location
      .equipments(Semantics::Lightbulb)
      .members
      .points(Semantics::Switch)
      .ensure.off
  end
end

Finding a related item that doesn’t fit in the semantic model

# We can use custom tags to identify certain items that don't quite fit in the semantic model.
# The extensions to the Enumerable mentioned above can help in this scenario.

# In the following example, the TV `Equipment` has three `Points`. However, we are using custom tags
# `Application` and `Channel` to identify the corresponding points, since the semantic model
# doesn't have a specific property for them.

# Here, we use Enumerable#tagged
# to find the point with the custom tag that we want.

# Item model:
Group   gTVPower
Group   lLivingRoom                                 [ "LivingRoom" ]

Group   eTV             "TV"       (lLivingRoom)    [ "Television" ]
Switch  TV_Power        "Power"    (eTV, gTVPower)  [ "Switch", "Power" ]
String  TV_Application  "App"      (eTV)            [ "Control", "Application" ]
String  TV_Channel      "Channel"  (eTV)            [ "Control", "Channel" ]

# Rule:
rule 'Switch TV to Netflix on startup' do
  changed gTVPower.members, to: ON
  run do |event|
    application = event.item.points.tagged('Application').first
    application << 'netflix'
  end
end

Find all semantic entities regardless of hierarchy

# All locations
items.locations

# All rooms
items.locations(Semantics::Room)

# All equipments
items.equipments

# All lightbulbs
items.equipments(Semantics::Lightbulb)

# All blinds
items.equipments(Semantics::Blinds)

# Turn off all "Power control"
items.points(Semantics::Control, Semantics::Power).off

# All items tagged "SmartLightControl"
items.tagged("SmartLightControl")

See Also:

Defined Under Namespace

Modules: SemanticTag, TagClassMethods Classes: Provider

Constant Summary collapse

Tag =
Deprecated.

Since openHAB 4.0, SemanticTag is the interface that all tags implement. Tags are simple instances, instead of another interface in a hierarchical structure.

This is a marker interface for all semantic tag classes.

org.openhab.core.semantics.Tag
Location =

This is the parent tag for all tags that represent a Location.

SemanticTag
Equipment =

This is the parent tag for all tags that represent an Equipment.

SemanticTag
Point =

This is the parent tag for all tags that represent a Point.

SemanticTag
Property =

This is the parent tag for all property tags.

SemanticTag

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#equipmentItem? (readonly)

Gets the related Equipment Item of this Item.

Checks ancestor groups one level at a time, returning the first Equipment Item found.

Returns:



506
507
508
# File 'lib/openhab/core/items/semantics.rb', line 506

def equipment
  Actions::Semantics.get_equipment(self)&.then(&Proxy.method(:new))
end

#equipment_typeSemanticTag? (readonly)

Returns the sub-tag of Equipment related to this Item.

In other words, the #semantic_type of this Item’s Equipment.

Returns:



519
520
521
# File 'lib/openhab/core/items/semantics.rb', line 519

def equipment_type
  translate_tag(Actions::Semantics.get_equipment_type(self))
end

#locationItem? (readonly)

Gets the related Location Item of this Item.

Checks ancestor groups one level at a time, returning the first Location Item found.

Returns:



480
481
482
# File 'lib/openhab/core/items/semantics.rb', line 480

def location
  Actions::Semantics.get_location(self)&.then(&Proxy.method(:new))
end

#location_typeSemanticTag? (readonly)

Returns the sub-tag of Location related to this Item.

In other words, the #semantic_type of this Item’s Location.

Returns:



493
494
495
# File 'lib/openhab/core/items/semantics.rb', line 493

def location_type
  translate_tag(Actions::Semantics.get_location_type(self))
end

#point_typeSemanticTag? (readonly)

Returns the sub-tag of Point this Item is tagged with.

Returns:



530
531
532
# File 'lib/openhab/core/items/semantics.rb', line 530

def point_type
  translate_tag(Actions::Semantics.get_point_type(self))
end

#property_typeSemanticTag? (readonly)

Returns the sub-tag of Property this Item is tagged with.

Returns:



541
542
543
# File 'lib/openhab/core/items/semantics.rb', line 541

def property_type
  translate_tag(Actions::Semantics.get_property_type(self))
end

#semantic_typeSemanticTag? (readonly)

Returns the SemanticTag this Item is tagged with.

It will only return the first applicable Tag, preferring a sub-tag of Location, Equipment, or Point first, and if none of those are found, looks for a Property.

Returns:



555
556
557
# File 'lib/openhab/core/items/semantics.rb', line 555

def semantic_type
  translate_tag(Actions::Semantics.get_semantic_type(self))
end

Class Method Details

.add(**tags) ⇒ Array<SemanticTag> .add(label: nil, synonyms: "", description: "", **tags) ⇒ Array<SemanticTag>

Adds custom semantic tags.

Overloads:

  • .add(**tags) ⇒ Array<SemanticTag>

    Quickly add one or more semantic tags using the default label, empty synonyms and descriptions.

    Examples:

    Add one semantic tag ‘Balcony` whose parent is `Semantics::Outdoor` (Location)

    Semantics.add(Balcony: Semantics::Outdoor)

    Add multiple semantic tags

    Semantics.add(Balcony: Semantics::Outdoor,
                  SecretRoom: Semantics::Room,
                  Motion: Semantics::Property)

    Parameters:

    • **tags (kwargs)

      Pairs of ‘tag` => `parent` where tag is either a `Symbol` or a `String` for the tag to be added, and parent is either a SemanticTag, a `Symbol` or a `String` of an existing tag.

    Returns:

  • .add(label: nil, synonyms: "", description: "", **tags) ⇒ Array<SemanticTag>

    Add a custom semantic tag with extra details.

    Examples:

    Semantics.add(SecretRoom: Semantics::Room, label: "My Secret Room",
      synonyms: "HidingPlace", description: "A room that requires a special trick to enter")

    Parameters:

    • label (String, nil) (defaults to: nil)

      Optional label. When ‘nil`, infer the label from the tag name, converting `CamelCase` to `Camel Case`

    • synonyms (String, Symbol, Array<String,Symbol>) (defaults to: "")

      Additional synonyms to refer to this tag.

    • description (String) (defaults to: "")

      A longer description of the tag.

    • **tags (kwargs)

      Exactly one pair of ‘tag` => `parent` where tag is either a `Symbol` or a `String` for the tag to be added, and parent is either a SemanticTag, a `Symbol` or a `String` of an existing tag.

    Returns:

Returns:

Raises:

  • (ArgumentError)

Since:

  • openHAB 4.0



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/openhab/core/items/semantics.rb', line 302

def add(label: nil, synonyms: "", description: "", **tags)
  raise ArgumentError, "Tags must be specified" if tags.empty?
  if (tags.length > 1) && !(label.nil? && synonyms.empty? && description.empty?)
    raise ArgumentError, "Additional options can only be specified when creating one tag"
  end

  synonyms = Array.wrap(synonyms).map { |s| s.to_s.strip }

  tags.map do |name, parent|
    if (existing_tag = lookup(name))
      logger.warn("Tag already exists: #{existing_tag.inspect}")
      next
    end

    unless parent.is_a?(SemanticTag)
      parent_tag = lookup(parent)
      raise ArgumentError, "Unknown parent: #{parent}" unless parent_tag

      parent = parent_tag
    end

    new_tag = org.openhab.core.semantics.SemanticTagImpl.new("#{parent.uid}_#{name}",
                                                             label,
                                                             description,
                                                             synonyms)
    Provider.instance.add(new_tag)
    lookup(name)
  end.compact
end

.lookup(id, locale = java.util.Locale.default) ⇒ SemanticTag, ...

Finds a semantic tag using its name, label, or synonyms.

Parameters:

  • id (String, Symbol)

    The tag name, label, or synonym to look up

  • locale (java.util.Locale) (defaults to: java.util.Locale.default)

    The locale of the given label or synonym

Returns:

  • (SemanticTag, Module, nil)

    The semantic tag class if found, or nil if not found. In openHAB 4.0, a SemanticTag instance will be returned. In openHAB 3.4, it will be a Module.



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/openhab/core/items/semantics.rb', line 224

def lookup(id, locale = java.util.Locale.default)
  id = id.to_s

  # @deprecated OH3.4 - the Property tag had an ID of "MeasurementProperty" in OH3.4.
  # This was corrected in OH4.
  id = "MeasurementProperty" if id == "Property" && Core.version < Core::V4_0

  # @deprecated OH3.4 missing registry
  if Provider.registry
    tag_class = service.get_by_label_or_synonym(id, locale).first ||
                Provider.registry.get_tag_class_by_id(id)
    return unless tag_class

    Provider.registry.get(Provider.registry.class.build_id(tag_class))
  else
    tag = org.openhab.core.semantics.SemanticTags.get_by_label_or_synonym(id, locale).first ||
          org.openhab.core.semantics.SemanticTags.get_by_id(id)
    return unless tag

    tag = tag.ruby_class
    tag.singleton_class.include(TagClassMethods)
    tag
  end
end

.remove(*tags, recursive: false) ⇒ Array<SemanticTag>

Removes custom semantic tags.

Parameters:

  • tags (SemanticTag, String, Symbol)

    Custom Semantic Tags to remove. The built in Semantic Tags cannot be removed.

  • recursive (true, false) (defaults to: false)

    Remove all children of the given tags.

Returns:

Raises:

  • (ArgumentError)

    if any of the tags have children

  • (FrozenError)

    if any of the tags are not custom tags

Since:

  • openHAB 4.0



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/openhab/core/items/semantics.rb', line 345

def remove(*tags, recursive: false)
  tags.flat_map do |tag|
    tag = lookup(tag) unless tag.is_a?(SemanticTag)
    next unless tag

    provider = Provider.registry.provider_for(tag)
    unless provider.is_a?(ManagedProvider)
      raise FrozenError, "Cannot remove item #{tag} from non-managed provider #{provider.inspect}"
    end

    children = []
    Provider.registry.providers.grep(ManagedProvider).each do |managed_provider|
      managed_provider.all.each do |existing_tag|
        next unless existing_tag.parent_uid == tag.uid
        raise ArgumentError, "Cannot remove #{tag} because it has children" unless recursive

        children += remove(existing_tag, recursive: recursive)
      end
    end

    remove_const(tag.name) if provider.remove(tag.uid) && const_defined?(tag.name)
    [tag] + children
  end.compact
end

.tagsArray<SemanticTag>

Returns all available Semantic tags

Returns:



200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/openhab/core/items/semantics.rb', line 200

def tags
  # @deprecated OH3.4 missing registry
  if Provider.registry
    Provider.registry.all.to_a
  else
    java.util.stream.Stream.of(
      org.openhab.core.semantics.model.point.Points.stream,
      org.openhab.core.semantics.model.property.Properties.stream,
      org.openhab.core.semantics.model.equipment.Equipments.stream,
      org.openhab.core.semantics.model.location.Locations.stream
    ).flat_map(&:itself).map(&:ruby_class).iterator.to_a
  end
end

Instance Method Details

#equipment?true, false

Checks if this Item is an Equipment

This is implemented as checking if the item’s #semantic_type is an Equipment. I.e. an Item has a single #semantic_type.

Returns:

  • (true, false)


446
447
448
# File 'lib/openhab/core/items/semantics.rb', line 446

def equipment?
  Actions::Semantics.equipment?(self)
end

#location?true, false

Checks if this Item is a Location

This is implemented as checking if the item’s #semantic_type is a Location. I.e. an Item has a single #semantic_type.

Returns:

  • (true, false)


434
435
436
# File 'lib/openhab/core/items/semantics.rb', line 434

def location?
  Actions::Semantics.location?(self)
end

#point?true, false

Checks if this Item is a Point

This is implemented as checking if the item’s #semantic_type is a Point. I.e. an Item has a single #semantic_type.

Returns:

  • (true, false)


457
458
459
# File 'lib/openhab/core/items/semantics.rb', line 457

def point?
  Actions::Semantics.point?(self)
end

#points(*point_or_property_types) ⇒ Array<Item>

Return the related Point Items.

Searches this Equipment Item for Points that are tagged appropriately.

If called on a Point Item, it will automatically search for sibling Points (and remove itself if found).

Examples:

Get all points for a TV

eGreatTV.points

Search an Equipment item for its switch

eGuestFan.points(Semantics::Switch) # => [GuestFan_Dimmer]

Search a Thermostat item for its current temperature item

eFamilyThermostat.points(Semantics::Status, Semantics::Temperature)
# => [FamilyThermostat_AmbTemp]

Search a Thermostat item for is setpoints

eFamilyThermostat.points(Semantics::Control, Semantics::Temperature)
# => [FamilyThermostat_HeatingSetpoint, FamilyThermostat_CoolingSetpoint]

Given a A/V receiver’s input item, search for its power item

FamilyReceiver_Input.points(Semantics::Switch) # => [FamilyReceiver_Switch]

Parameters:

  • point_or_property_types (SemanticTag)

    Pass 1 or 2 classes that are sub-classes of Point or Property. Note that when comparing against semantic tags, it does a sub-class check. So if you search for [Control], you’ll get items tagged with [Switch].

Returns:



586
587
588
589
590
591
592
593
# File 'lib/openhab/core/items/semantics.rb', line 586

def points(*point_or_property_types)
  return members.points(*point_or_property_types) if equipment? || location?

  # automatically search the parent equipment (or location?!) for sibling points
  result = (equipment || location)&.points(*point_or_property_types) || []
  result.delete(self)
  result
end

#semantic?true, false

Checks if this Item has any semantic tags

Returns:

  • (true, false)


466
467
468
# File 'lib/openhab/core/items/semantics.rb', line 466

def semantic?
  !!semantic_type
end