# hubitat_devices.yaml
# Copyright (C) 2021-2026 Kedron Holdings LLC, All Rights Reserved
# This file is part of Reactor.
#
# *** IMPORTANT ***
# This is a system file. DO NOT place overrides or make modifications here.
# You can add or override implementation section items by specifying them
# in local.HassController.implementation in your site configuration.
# You can add or override mapping section items by specifying your data in
# local.HassController.mapping in your site configuration.
---
version: 26146
revision: 1
format: 1
copyright: Copyright (C) 2020-2026 Kedron Holdings LLC, All Rights Reserved
requires: 25315

implementation:
  power_switch:
    attributes:
      state:
        attribute: switch
        expr: value != "off"
    actions:
      'on':
        path: "/devices/%id%/on"
      'off':
        path: "/devices/%id%/off"
      'set': true
  toggle:
    actions:
      toggle: true  # use the default system implementation
  dimming:
    attributes:
      level:
        attribute: level
        expr: '"off" == (attributes.switch ?? "on" ) ? 0 : (value ?# 0) / 100.0'
    actions:
      # up and down use the default system implementation
      set:
        # If we're setting dimming level to 0, remap to direct off
        remap:
          - condition: "(parameters.level ?# 0) == 0"
            action: power_switch.off
        path: "/devices/%id%/setLevel/%round((parameters.level ?? 0)*100)%"
  rgb_color:
    # set color to blue (hue 0-100):
    # http://IP/apps/api/X/devices/N/setColor/%7B%22hue%22:66,%22saturation%22:100,%22level%22:100%7D?access_token=
    attributes:
      red:
        attribute: hue
        expr: "hsltorgb( value * 3.6, attributes.saturation / 100, attributes.level / 100 )[0]"
      green:
        attribute: hue
        expr: "hsltorgb( value * 3.6, attributes.saturation / 100, attributes.level / 100 )[1]"
      blue:
        attribute: hue
        expr: "hsltorgb( value * 3.6, attributes.saturation / 100, attributes.level / 100 )[2]"
    actions:
      set_rgb:
        path: "/devices/%id%/setColor/%he_colormap( rgbtohsl( parameters.red, parameters.green, parameters.blue ) )%"
      set:
        path: >
          /devices/%id%/setColor/%he_colormap( rgbtohsl( (int(parameters.value)>>16) & 255,
          (int(parameters.value)>>8) & 255, parameters.value & 255 ) )%
  hs_color:
    attributes:
      hue:
        attribute: hue
        expr: float( value ?# 0 ) * 3.6
      saturation:
        attribute: saturation
        expr: float( value ?# 0 ) / 100
    actions:
      set_hs:
        path: >
          /devices/%id%/setColor/%he_colormap( [ parameters.hue, parameters.saturation,
          entity.attributes.dimming.level ?? 0.5 ] )%
      set_hue:
        path: "/devices/%id%/setHue/%floor(parameters.hue / 3.6)%"
      set_saturation:
        path: "/devices/%id%/setSaturation/%floor(parameters.level*100)%"
  color_temperature:
    attributes:
      value:
        attribute: colorTemperature
    actions:
      set:
        path: "/devices/%id%/setColorTemperature/%parameters.value%"
  x_hubitat_color:
    attributes:
      color_mode:
        attribute: colorMode
      color_name:
        attribute: colorName
    actions:
      set_hsl:
        path: "/devices/%id%/setColor/%he_colormap( [ parameters.hue, parameters.saturation, parameters.level ] )%"
  binary_sensor:
    attributes:
      state:
        expr: state != "off"
  motion_sensor:
    attributes:
      state:
        expr: attributes.motion != "inactive"
  smoke_detector:
    attributes:
      state:
        expr: attributes.smoke == "detected"
  co_detector:
    attributes:
      state:
        expr: attributes.carbonMonoxide == "detected"
  leak_detector:
    attributes:
      state:
        expr: attributes.water == "wet"
  door_sensor:
    attributes:
      state:
        expr: attributes.contact != 'closed'
  ContactSensor.binary_sensor:
    attributes:
      state:
        expr: attributes.contact != 'closed'
  SoundSensor.binary_sensor:
    attributes:
      state:
        expr: attributes.sound == "detected"
  AccelerationSensor.binary_sensor:
    attributes:
      state:
        expr: attributes.acceleration == "active"
  ShockSensor.binary_sensor:
    attributes:
      state:
        expr: attributes.shock == "detected"
  SleepSensor.binary_sensor:
    attributes:
      state:
        expr: attributes.sleeping == "sleeping"
  tamper:
    attributes:
      state:
        expr: attributes.tamper == "detected"
  temperature_sensor:
    attributes:
      value:
        attribute: temperature
        expr: "float( value ) ?# null"
      units: null
  humidity_sensor:
    attributes:
      value:
        attribute: humidity
        expr: "float( value ) ?# null"
      units: "%"
  battery_power:
    attributes:
      level:
        attribute: battery
        expr: "isnull( value ) ? null : ( ( value ?# 0 ) / 100 )"
        timestamp: since
  Beacon.binary_sensor:
    attributes:
      state:
        expr: attributes.present == 'present'
  presence_sensor:
    attributes:
      state:
        expr: attributes.presence == 'present'
  lock:
    attributes:
      state:
        expr: attributes.lock == "locked"
    actions:
      lock:
        command: lock
      unlock:
        command: unlock
      'set':
        remap:
          - condition: bool( parameters.state )
            action: lock.lock
          - action: lock.unlock
  uv_sensor:
    attributes:
      value:
        attribute: ultravioletIndex
        expr: "float( value ) ?# null"
      units: null
  air_quality_sensor:
    attributes:
      value:
        attribute: airQualityIndex
        expr: "int( value ) ?# null"
      units: null
  voltage_sensor:
    attributes:
      value:
        attribute: voltage
        expr: "float( value ) ?# null"
      units: "V"
  current_sensor:
    attributes:
      value:
        attribute: amperage
        expr: "float( value ) ?# null"
      units: "A"
  current_sensor#current:  # alternate form used by Aeotec SmartSwitch 6 (not to spec)
    attributes:
      value:
        attribute: current
        expr: "float( value ) ?# null"
      units: "A"
  power_sensor:
    attributes:
      value:
        attribute: power
        expr: "float( value ) ?# null"
      units: "W"
  energy_sensor:
    attributes:
      value:
        attribute: energy
        expr: "float( value ) ?# null"
      units: "KWh"
  light_sensor:
    attributes:
      value:
        attribute: illuminance
        expr: "float( value ) ?# null"
  pHMeasurement.value_sensor:
    attributes:
      value:
        attribute: pH
        expr: "float( value ) ?# null"
      units: null
  CarbonDioxideMeasurement.value_sensor:
    attributes:
      value:
        attribute: carbonDioxide
        expr: "float( value ) ?# null"
      units: 'ppm'
  PressureMeasurement.pressure_sensor:
    attributes:
      value:
        attribute: pressure
        expr: "float( value ) ?# null"
      units:
        attribute: unit
  SoundPressureLevel.value_sensor:
    attributes:
      value:
        attribute: soundPressureLevel
        expr: "float( value ) ?# null"
      units: "dB"
  TouchSensor.binary_sensor:
    attributes:
      state:
        attribute: touch
        expr: lower(value) == 'touched'
  cover:
    attributes:
      state:
        attribute: windowShade
        expr: lower(value) != "closed"
    actions:
      open:
        command: open
      close:
        command: close
      stop: false
  keypad:
    attributes:
      last_slot: null
      last_code: null
      last_function: null
      since: null
    actions:
      set_code:
        path: "/devices/%id%/setCode/%parameters.slot%,%parameters.code%,%parameters.name%"
      delete_code:
        path: "/devices/%id%/deleteCode/%parameters.slot%"
  WindowShade.position:
    attributes:
      value:
        attribute: position
        expr: "isnull( value ) ? null : ( ( value ?# 0 ) / 100 )"
    actions:
      increase:
        path: "/devices/%id%/setPosition/%min( 1, entity.attributes.position.value + 0.1 )%"
      decrease:
        path: "/devices/%id%/setPosition/%max( 0, entity.attributes.position.value - 0.1 )%"
      relative:
        path: >
          /devices/%id%/setPosition/%min( 1, max( 0,
          entity.attributes.position.value + ( parameters.amount ?? 0 ) ) )%
      set:
        path: "/devices/%id%/setPosition/%parameters.value%"
  hvac_control:
    attributes:
      mode:
        # Our values are superset of HEs, so no mapping
        attribute: thermostatMode
      state:
        # Our values are superset of HEs, so no mapping
        attribute: thermostatOperatingState
    actions:
      set_mode:
        path: "/devices/%id%/setThermostatMode/%parameters.mode%"
  hvac_heating_unit:
    attributes:
      state:
        attribute: thermostatOperatingState
        expr: "value == 'heating'"
      setpoint:
        attribute: heatingSetpoint
        expr: "float( value ) ?# null"
      units: null
    actions:
      set_setpoint:
        path: "/devices/%id%/setHeatingSetpoint/%parameters.setpoint%"
  hvac_cooling_unit:
    attributes:
      state:
        attribute: thermostatOperatingState
        expr: "value == 'cooling'"
      setpoint:
        attribute: coolingSetpoint
        expr: "float( value ) ?# null"
      units: null
    actions:
      set_setpoint:
        path: "/devices/%id%/setCoolingSetpoint/%parameters.setpoint%"
  hvac_blower_unit:
    attributes:
      state:
        attribute: thermostatFanOperatingState
        expr: "value == 'on'"
      mode:
        attribute: thermostatFanMode
        map:
          'off': 'off'
          'on': 'continuous'
          'auto': 'auto'
      filter_change: null
    actions:
      set_mode:
        path: "/devices/%id%/setThermostatFanMode/%({'continuous':'on'})[parameters.mode] ?? parameters.mode%"
  x_hubitat_momentary:
    actions:
      push:
        command: push
  x_hubitat_pushable:
    attributes:
      num_buttons:
        attribute: numberOfButtons
      pushed:
        attribute: pushed
        timestamp: since
  x_hubitat_holdable:
    attributes:
      held:
        attribute: held
        timestamp: since
  x_hubitat_releaseable:
    attributes:
      released:
        attribute: released
        timestamp: since
  x_hubitat_doubletap:
    atttributes:
      doubletapped:
        attribute: doubleTapped
        timestamp: since
  x_hubitat_filter:
    attributes:
      check:
        attribute: filterStatus
        expr: lower(value) == "replace"
  x_hubitat_energymeter:
    attributes:
      energy:
        attribute: energy
        expr: "float( value ) ?# null"
  x_hubitat_healthcheck: {}  # stub
  x_hubitat_sys_mode:
    attributes: {}
    actions:
      set:
        path: "/modes/%entity.attributes.x_hubitat_sys_mode.modes[parameters?.mode] ?? parameters?.mode ?? 1%"
  x_hubitat_hsm:
    actions:
      arm:
        path: "/hsm/%({home:'armedHome', away:'armedAway', night:'armedNight'})[parameters?.mode] ?? parameters?.mode%"
      disarm:
        path: "/hsm/disarm"
      arm_away:
        path: "/hsm/armAway"
      arm_home:
        path: "/hsm/armHome"
      arm_night:
        path: "/hsm/armNight"
  volume:
    attributes:
      level:
        attribute: volume
        expr: "isnull( value ) ? null : ( ( value ?# 0 ) / 100 )"
    actions:
      increase:
        path: "/devices/%id%/setVolume/%min( 1, entity.attributes.volume.level + ( parameters.amount ?? 0.05 ) ) * 100%"
      decrease:
        path: "/devices/%id%/setVolume/%max( 1, entity.attributes.volume.level - ( parameters.amount ?? 0.05 ) ) * 100%"
      relative:
        path: "/devices/%id%/setVolume/%max( 0, min( 1, entity.attributes.volume.level + ( parameters.amount ?? 0 ) ) ) * 100%"
      set:
        path: "/devices/%id%/setVolume/%max( 0, min( 100, ( parameters.value ?? 0 ) * 100 ) )%"
      set_db: false
  muting:
    attributes:
      state:
        attribute: mute
        expr: "isnull( value ) ? null : ( value === `muted` )"
    actions:
      mute:
        path: "/devices/%id%/mute"
      unmute:
        path: "/devices/%id%/unmute"
      toggle:
        path: "/devices/%id%/% entity.attributes.muting.state ? `unmute` : `mute`%"
      set:
        path: "/devices/%id%/% parameters.muting ? `mute` : `unmute`%"
  MediaTransport.av_transport:
    attributes:
      state:
        # Note MusicPlayer uses "status" where MediaTransport uses "transportStatus". Thx guys.
        expr: >
          ({`playing`:`PLAYING`,`paused`:`PAUSED`,`stopped`:`STOPPED`})[ attributes.transportStatus ?? `` ] ?? `unknown`
      current_track: null
      current_title: null
      current_artist: null
      current_album: null
      album_art: null
    actions:
      stop:
        path: "/devices/%id%/stop"
      pause:
        path: "/devices/%id%/pause"
      play:
        path: "/devices/%id%/play"
      fast_forward: false
      rewind: false
      play_media:
        path: "/devices/%id%/playTrack/%urlencode(parameters.media_url??'')%"
  MusicPlayer.av_transport:
    attributes:
      state:
        # Note MusicPlayer uses "status" where MediaTransport uses "transportStatus". Thx guys.
        expr: >
          ({`playing`:`PLAYING`,`paused`:`PAUSED`,`stopped`:`STOPPED`})[ attributes.status ?? `` ] ?? `unknown`
      current_track:
        attribute: trackData
        expr: "( parseJSON( value ) )?.trackNumber"
      current_title:
        attribute: trackDescription
      current_artist:
        attribute: trackData
        expr: "( parseJSON( value ) )?.artist"
      current_album:
        attribute: trackData
        expr: "( parseJSON( value ) )?.album"
      album_art: null
    actions:
      stop:
        path: "/devices/%id%/stop"
      pause:
        path: "/devices/%id%/pause"
      play:
        path: "/devices/%id%/play"
      fast_forward: false
      rewind: false
      play_media:
        path: "/devices/%id%/playTrack/%urlencode(parameters.media_url??'')%"
  media_navigation:
    actions:
      next_track:
        path: "/devices/%id%/nextTrack"
      previous_track:
        path: "/devices/%id%/previousTrack"
      next_chapter:
        path: "/devices/%id%/nextTrack"
      previous_chapter:
        path: "/devices/%id%/previousTrack"
      next_media:
        path: "/devices/%id%/nextTrack"
      previous_media:
        path: "/devices/%id%/previousTrack"
  Variable.string_sensor:
    attributes:
      value:
        attribute: variable

# https://docs.hubitat.com/index.php?title=Driver_Capability_List
capabilities:
  AccelerationSensor:
    - binary_sensor
  Actuator:
  AirQuality:
    - air_quality_sensor
  Alarm:
  AudioNotification:
  AudioVolume:
    - volume
    - muting
  Battery:
    - battery_power
  Beacon:
    - binary_sensor
  Bulb:
    - power_switch
    - toggle
  Button:  # deprecated
  CarbonDioxideMeasurement:
    - value_sensor
  CarbonMonoxideDetector:
    - co_detector
  ChangeLevel:
  Chime:
  ColorControl:
    - rgb_color
    - hs_color
  ColorMode:
    - x_hubitat_color
  ColorTemperature:
    - color_temperature
  Configuration:
  Consumable:
  ContactSensor:
    - door_sensor
    - binary_sensor
  CurrentMeter:
    - current_sensor
  CurrentMeter#current:
    - current_sensor#current
  DoorControl:
  DoubleTapableButton:
    - x_hubitat_doubletap
  EnergyMeter:
    - energy_sensor
    - x_hubitat_energymeter
  EstimatedTimeOfArrival:
  FanControl:
  FilterStatus:
    - x_hubitat_filter
  GarageDoorControl:
  HealthCheck:
    - x_hubitat_healthcheck
  HoldableButton:
    - x_hubitat_holdable
  IlluminanceMeasurement:
    - light_sensor
  ImageCapture:
  Indicator:
  Initialize:
  Light:
    - power_switch
    - toggle
  LightEffects:
  LocationMode:
  Lock:
    - lock
  LockCodes:
    - keypad
  MediaController:
  Momentary:
    - x_hubitat_momentary
  MotionSensor:
    - motion_sensor
  MediaTransport:
    - av_transport
  MusicPlayer:
    - av_transport
    - media_navigation
  Notification:
  Outlet:
  pHMeasurement:
    - value_sensor
  Polling:
  PowerMeter:
    - power_sensor
  PowerSource:
  PresenceSensor:
    - presence_sensor
  PressureMeasurement:
    - pressure_sensor
  PushableButton:
    - x_hubitat_pushable
  Refresh:
  RelativeHumidityMeasurement:
    - humidity_sensor
  RelaySwitch:
    - power_switch
    - toggle
  ReleasableButton:
    - x_hubitat_releaseable
  SamsungTV:
  SecurityKeypad:
  Sensor:
  ShockSensor:
    - binary_sensor
  SignalStrength:
  SleepSensor:
    - binary_sensor
  SmokeDetector:
    - smoke_detector
  SoundPressureLevel:
    - value_sensor
  SoundSensor:
    - binary_sensor
  SpeechRecognition:
  SpeechSynthesis:
  StepSensor:
  Switch:
    - power_switch
    - toggle
  SwitchLevel:
    - dimming
  TamperAlert:
    - tamper
  Telnet:
  TemperatureMeasurement:
    - temperature_sensor
  TestCapability:
  Thermostat:
    - hvac_control
    - hvac_heating_unit
    - hvac_cooling_unit
    - hvac_blower_unit
    - temperature_sensor
    - humidity_sensor
  ThermostatCoolingSetpoint:
  ThermostatFanMode:
  ThermostatHeatingSetpoint:
  ThermostatMode:
  ThermostatOperatingState:
  ThermostatSchedule:
  ThermostatSetpoint:
  ThreeAxis:
  TimedSession:
  Tone:
  TouchSensor:
    - binary_sensor
  TV:
  UltravioletIndex:
    - uv_sensor
  Valve:
  Variable:
    - string_sensor
  VideoCamera:
  VideoCapture:
  VoltageMeasurement:
    - voltage_sensor
  WaterSensor:
    - leak_detector
  WindowShade:
    - cover
    - position
  ZwMultiChannel:

# https://docs.hubitat.com/index.php?title=Driver_Capability_List
native_capabilities:
  AccelerationSensor:
    attributes:
      acceleration:
        type: string
        values: ["inactive", "active"]
  Actuator: {}  # has no attributes or actions
  AirQuality:
    attributes:
      airQualityIndex:
        type: int
        min: 0
        max: 500
  Alarm:
    attributes:
      alarm:
        type: string
        values:
          - strobe
          - 'off'
          - both
          - siren
    actions:
      'off': {}
      siren: {}
      strobe: {}
      both: {}
  AudioNotification:
    actions:
      playText:
        arguments:
          text:
            type: string
            required: true
            sort: 1
          volumelevel:
            type: int
            sort: 2
      playTextAndRestore:
        arguments:
          text:
            type: string
            required: true
            sort: 1
          volumelevel:
            type: int
            sort: 2
      playTextAndResume:
        arguments:
          text:
            type: string
            required: true
            sort: 1
          volumelevel:
            type: int
            sort: 2
      playTrack:
        arguments:
          trackuri:
            type: string
            required: true
            sort: 1
          volumelevel:
            type: int
            sort: 2
      playTrackAndRestore:
        arguments:
          trackuri:
            type: string
            required: true
            sort: 1
          volumelevel:
            type: int
            sort: 2
      playTrackAndResume:
        arguments:
          trackuri:
            type: string
            required: true
            sort: 1
          volumelevel:
            type: int
            sort: 2
  AudioVolume:
    attributes:
      mute:
        type: string
        values: ["unmuted", "muted"]
      volume:
        type: int
        min: 0
        max: 100
    actions:
      mute: {}
      unmute: {}
      setVolume:
        arguments:
          volumelevel:
            type: int
            required: true
      volumeUp: {}
      volumeDown: {}
  Battery:
    attributes:
      battery:
        type: int
        min: 0
        max: 100
  Beacon:
    attributes:
      presence:
        type: string
        values: ["not present", "present"]
  Bulb:
    attributes:
      switch:
        type: string
        values: ["on", "off"]
    actions:
      'off': {}
      'on': {}
  Button:  # deprecated
    attributes:
      button:
        type: int
      holdableButton:
        type: string
        values: ["true", "false"]
      numberOfButtons:
        type: int
  CarbonMonoxideDetector:
    attributes:
      carbonDioxide:
        type: string
        values: ["clear", "tested", "detected"]
  CarbonDioxideMeasurement:
    attributes:
      carbonDioxide:
        type: int
  ChangeLevel:
    actions:
      startLevelChange:
        arguments:
          direction:
            type: string  # enum values not given in docs
            required: true
      stopLevelChange: {}
  Chime:
    attributes:
      soundEffects:
        type: string
      soundName:
        type: string
      status:
        type: string
        values: ["playing", "stopped"]
    actions:
      playSound:
        arguments:
          soundnumber:
            type: int
            required: true
      stop: {}
  ColorControl:
    attributes:
      rgb: {}
      color: {}
      colorName: {}
      hue:
        type: int
        min: 0
        max: 360
      saturation:
        type: int
        min: 0
        max: 100
    actions:
      setColor:
        arguments:
          colormap:
            type: string
            requred: true
      setHue:
        arguments:
          hue:
            type: int
      setSaturation:
        arguments:
          saturation:
            type: int
  ColorMode:
    attributes:
      colorMode:
        type: string
        values: ["CT", "RGB", "EFFECTS"]
  ColorTemperature:
    attributes:
      colorName:
        type: string
      colorTemperature:
        type: int
    actions:
      setColorTemperature:
        arguments:
          colortemperature:
            type: int
            required: true
            sort: 1
          level:
            type: int
            sort: 2
          transitionTime:
            type: real
            sort: 3
  Configuration:
    actions:
      configure: {}
  Consumable:
    attributes:
      consumableStatus:
        type: string
        values: ["missing", "order", "maintenance_required", "good", "replace"]
    actions:
      setConsumableStatus:
        arguments:
          status:
            required: true
  ContactSensor:
    attributes:
      contact:
        type: string
        values: ["closed", "open"]
  CurrentMeter:
    attributes:
      amperage:
        type: real
  DoorControl:
    attributes:
      door:
        values: ["unknown", "closed", "open", "closing", "opening"]
    actions:
      open: {}
      close: {}
  DoubleTapableButton:
    attributes:
      doubleTapped:
        type: int
    actions:
      doubleTap:
        arguments:
          buttonNumber:
            type: int
  EnergyMeter:
    attributes:
      energy:
        type: real
  EstimatedTimeOfArrival:
    attributes:
      eta: {}
  FanControl:
    attributes:
      speed:
        values: ["low", "medium-low", "medium-high", "high", "on", "off", "auto"]
      supportedFanSpeeds: {}
    actions:
      setSpeed:
        arguments:
          fanSpeed:
            required: true
      cycleSpeed: {}
  FilterStatus:
    attributes:
      filterStatus:
        values: ["normal", "replace"]
  GarageDoorControl:
    attributes:
      door:
        values: ["unknown", "open", "closing", "closed", "opening"]
    actions:
      close: {}
      open: {}
  GasDetector:
    attributes:
      naturalGas:
        values: ["clear", "tested", "detected"]
  HealthCheck:
    attributes:
      checkInterval:
        type: int
    actions:
      ping: {}
  HoldableButton:
    attributes:
      held:
        type: int
    actions:
      hold:
        arguments:
          buttonNumber:
            type: int
            required: true
  IlluminanceMeasurement:
    attributes:
      illuminance:
        type: int
  ImageCapture:
    attributes:
      image: {}
    actions:
      take: {}
  Indicator:
    attributes:
      indicatorStatus:
        values: ["never", "when on", "when off"]
    actions:
      indicatorNever: {}
      indicatorWhenOff: {}
      indicatorWhenOn: {}
  Initialize:
    actions:
      initialize: {}
  LevelPreset:
    attributes:
      levelPreset:
        int: number
        min: 1
        max: 100
    actions:
      presetLevel:
        arguments:
          level:
            type: int
  Light:
    attributes:
      switch:
        values: ["on", "off"]
    actions:
      'off': {}
      'on': {}
  LightEffects:
    attributes:
      effectName: {}
      lightEffects: {}
    actions:
      setEffect:
        arguments:
          effectNumber:
            type: int
      setNextEffect: {}
      setPreviousEffect: {}
  LiquidFlowRate:
    attributes:
      rate:
        type: real
  LocationMode:
    attributes:
      mode: {}
  Lock:
    attributes:
      lock:
        values: ["locked", "unlocked with timeout", "unlocked", "unknown"]
    actions:
      lock: {}
      unlock: {}
  LockCodes:
    attributes:
      codeChanged:
        values: ["added", "changed", "deleted", "failed"]
      codeLength:
        type: int
      lockCodes: {}
      maxCodes:
        type: int
    actions:
      deleteCode:
        arguments:
          codeposition:
            type: int
            required: true
      getCodes: {}
      setCode:
        arguments:
          codeposition:
            type: int
            required: true
            sort: 1
          pincode:
            type: string
            required: true
            sort: 2
          name:
            type: string
            sort: 3
      setCodeLength:
        arguments:
          pincodelength:
            type: int
  MediaController:
    attributes:
      activities: {}
      currentActivity: {}
    actions:
      getAllActivities: {}
      getCurrentActivity: {}
      startActivity:
        arguments:
          activityname:
            required: true
  MediaInputSource:
    attributes:
      supportedInput: {}
      mediaInputSource: {}
    actions:
      setInputSource:
        arguments:
          inputName:
            required: true
  MediaTransport:
    attributes:
      transportStatus:
        values: ["playing", "paused", "stopped"]
    actions:
      play: {}
      pause: {}
      stop: {}
  Momentary:
    actions:
      push: {}
  MotionSensor:
    attributes:
      motion:
        values: ["inactive", "active"]
  MusicPlayer:
    attributes:
      level:
        type: int
      mute:
        values: ["unmuted", "muted"]
      status: {}
      trackData: {}
      trackDescription: {}
    actions:
      mute: {}
      nextTrack: {}
      pause: {}
      play: {}
      playText:
        arguments:
          text:
            required: true
      playTrack:
        arguments:
          trackuri:
            required: true
      previousTrack: {}
      restoreTrack:
        arguments:
          trackuri:
            required: true
      resumeTrack:
        arguments:
          trackuri:
            required: true
      setLevel:
        arguments:
          volumelevel:
            type: int
            required: true
      setTrack:
        arguments:
          trackuri:
            required: true
      stop: {}
      unmute: {}
  Notification:
    actions:
      deviceNotification:
        arguments:
          text:
            required: true
  Outlet:
    attributes:
      switch:
        values: ["on", "off"]
    actions:
      'on': {}
      'off': {}
  pHMeasurement:
    attributes:
      pH:
        type: real
  Polling:
    actions:
      poll: {}
  PowerMeter:
    attributes:
      power:
        type: real
      powerLow:
        type: real
      powerHigh:
        type: real
  PowerSource:
    attributes:
      powerSource:
        values: ["battery", "dc", "mains", "unknown"]
  PresenceSensor:
    attributes:
      presence:
        values: ["present", "not present"]
  PressureMeasurement:
    attributes:
      pressure:
        type: real
  PushableButton:
    attributes:
      numberOfButtons:
        type: int
      pushed:
        type: int
    actions:
      push:
        arguments:
          buttonNumber:
            type: int
            required: true
  Refresh:
    actions:
      refresh: {}
  RelativeHumidityMeasurement:
    attributes:
      humidity:
        type: real
  RelaySwitch:
    attributes:
      switch:
        values: ["on", "off"]
    actions:
      'on': {}
      'off': {}
  ReleasableButton:
    attributes:
      released:
        type: int
    actions:
      release:
        arguments:
          buttonNumber:
            type: int
            required: true
  SamsungTV:
    attributes:
      messageButton: {}
      mute:
        values: ["muted", "unknown", "unmuted"]
      pictureMode:
        values: ["unknown", "standard", "movie", "dynamic"]
      soundMode:
        values: ["speech", "movie", "unknown", "standard", "music"]
      switch:
        values: ["on", "off"]
      volume:
        type: int
    actions:
      mute: {}
      'off': {}
      'on': {}
      setPictureMode:
        arguments:
          pictureMode: {}
          required: true
      setSoundMode:
        arguments:
          soundMode: {}
          required: true
      setVolume:
        arguments:
          volume:
            type: int
            required: true
      showMessage:
        arguments:
          string1:
            sort: 1
          string2:
            sort: 2
          string3:
            sort: 3
          string4:
            sort: 4
  SecurityKeypad:
    arguments:
      codeChanged:
        values: ["added", "changed", "deleted", "failed"]
      codeLength:
        type: int
      lockCodes: {}
      maxCodes:
        type: int
      securityKeypad:
        values: ["disarmed", "armed home", "armed away", "unknown"]
    actions:
      armAway: {}
      armHome: {}
      deleteCode:
        arguments:
          codeposition:
            type: int
            required: true
      disarm: {}
      getCodes: {}
      setCode:
        arguments:
          codeposition:
            type: int
            required: true
            sort: 1
          pincode:
            required: true
            sort: 2
          name:
            sort: 3
      setCodeLength:
        arguments:
          type: int
          required: true
      setEntryDelay:
        arguments:
          type: int
          required: true
      setExitDelay:
        arguments:
          type: int
          required: true
  Sensor: {}
  ShockSensor:
    attributes:
      shock:
        values: ["clear", "detected"]
  SignalStrength:
    attributes:
      lqi:
        type: real
      rssi:
        type: real
  SleepSensor:
    attributes:
      sleeping:
        values: ["sleeping", "not sleeping"]
  SmokeDetector:
    attributes:
      smoke:
        values: ["clear", "tested", "detected"]
  SoundPressureLevel:
    attributes:
      soundPressureLevel: real
  SoundSensor:
    attributes:
      sound:
        values: ["detected", "detected"]
  SpeechRecognition:
    attributes:
      phraseSpoken: {}
  SpeechSynthesis:
    actions:
      speak:
        arguments:
          text:  # Beware: https://community.hubitat.com/t/node-red-nodes-for-hubitat/34386/2138
            required: true
            sort: 1
          volume:
            type: int
            sort: 2
          voice:
            sort: 3
  StepSensor:
    attributes:
      goal:
        type: int
      steps:
        type: int
  Switch:
    attributes:
      switch:
        values: ["on", "off"]
    actions:
      'on': {}
      'off': {}
  SwitchLevel:
    attributes:
      level:
        type: int
        min: 0
        max: 100
    actions:
      setLevel:
        arguments:
          level:
            type: int
            required: true
            sort: 1
          duration:
            type: int
            sort: 2
  TamperAlert:
    attributes:
      tamper:
        values: ["clear", "detected"]
  Telnet:
    attributes:
      networkStatus:
        values: ["online", "offline"]
  TemperatureMeasurement:
    attributes:
      temperature:
        type: real
  TestCapability: {}
  Thermostat:
    attributes:
      coolingSetpoint:
        type: real
      heatingSetpoint:
        type: real
      schedule: {}  # deprecated, removed at 2.2.6
      supportedThermostatFanModes: {}
      supportedThermostatModes: {}
      temperature:
        type: real
      thermostatFanMode:
        values: ["on", "circulate", "auto"]
      thermostatMode:
        values: ["auto", "off", "heat", "emergency heat", "cool"]
      thermostatOperatingState:
        values: ["heating", "pending cool", "pending heat", "vent economizer", "idle", "cooling", "fan only"]
      thermostatSetpoint:
        type: real
    actions:
      auto: {}
      cool: {}
      emergencyHeat: {}
      fanAuto: {}
      fanCirculate: {}
      fanOn: {}
      heat: {}
      'off': {}
      setCoolingSetpoint:
        arguments:
          temperature:
            type: real
            required: true
      setHeatingSetpoint:
        arguments:
          temperature:
            type: real
            required: true
      setSchedule:  # deprecated, removed at 2.2.6
        arguments:
          schedule: {}
      setThermostatFanMode:
        arguments:
          fanmode:
            required: true
      setThermostatMode:
        arguments:
          mode:
            required: true
  ThermostatCoolingSetpoint:
    attributes:
      coolingSetpoint:
        type: real
    actions:
      setCoolingSetpoint:
        arguments:
          temperature:
            type: real
            required: true
  ThermostatFanMode:
    attributes:
      thermostatFanMode:
        values: ["auto", "circulate", "on"]
    actions:
      fanAuto: {}
      fanCirculate: {}
      fanOn: {}
      setThermostatFanMode:
        arguments:
          fanmode:
            required: true
  ThermostatHeatingSetpoint:
    attributes:
      heatingSetpoint:
        type: real
    actions:
      setHeatingSetpoint:
        arguments:
          temperature:
            type: real
            required: true
  ThermostatMode:
    atttributes:
      thermostatMode:
        values: ["auto", "off", "heat", "emergency heat", "cool"]
    actions:
      auto: {}
      cool: {}
      emergencyHeat: {}
      heat: {}
      'off': {}
      setThermostatMode:
        arguments:
          mode:
            required: true
  ThermostatOperatingState:
    attributes:
      thermostatOperatingState:
        values: ["heating", "pending cool", "pending heat", "vent economizer", "idle", "cooling", "fan only"]
  ThermostatSchedule:
    attributes:
      schedule: {}
    actions:
      setSchedule:
        arguments:
          schedule:
            required: true
  ThermostatSetpoint:
    attributes:
      thermostatSetpoint:
        type: real
  ThreeAxis:
    attributes:
      vector3: {}
  TimedSession:
    attributes:
      sessionStatus:
        values: ["stopped", "cancelled", "running", "paused"]
      timeRemaining:
        real
    actions:
      cancel: {}
      pause: {}
      setTimeRemaining:
        arguments:
          time:
            type: real
            required: true
      start: {}
      stop: {}
  Tone:
    actions:
      beep: {}
  TouchSensor:
    attributes:
      touch:
        values: ["clear", "touched"]  # doc actually lists clear only -- momentary to empty/null?
  TV:
    attributes:
      channel:
        type: int
      movieMode: {}
      picture: {}
      power: {}
      sound: {}
      volume:
        type: int
    actions:
      channelDown: {}
      channelUp: {}
      volumeDown: {}
      volumeUp: {}
  UltravioletIndex:
    attributes:
      ultravioletIndex:
        type: real
  Valve:
    attributes:
      valve:
        values: ["open", "closed"]
    actions:
      open: {}
      close: {}
  Variable:
    attributes:
      variable: {}
    actions:
      setVariable:
        warning: >
          Hubitat strings are limited to 1024 characters (255 for hub firmware <2.3.5.135),
          and may not contain commas. If you send a longer string, the action will not work.
        arguments:
          valueToSet:
            required: true
  VideoCamera:
    attributes:
      camera:
        values: ["off", "on", "restarting", "unavailable"]
      mute:
        values: ["unmuted", "muted"]
      settings: {}
      statusMessage: {}
    actions:
      flip: {}
      mute: {}
      'off': {}
      'on': {}
      unmute: {}
  VideoCapture:
    attributes:
      clip: {}
    actions:
      capture:
        arguments:
          date1:
            sort: 1
          date2:
            sort: 2
          date3:
            sort: 3
  VoltageMeasurement:
    attributes:
      voltage:
        type: real
      voltageLow:
        type: real
      voltageHigh:
        type: real
      frequency:
        type: real
  WaterSensor:
    attributes:
      water:
        values: ["wet", "dry"]
  WindowBlind:
    attributes:
      position:
        type: int
      windowShade:
        values: ["opening", "partially open", "closed", "open", "closing", "unknown"]
      tilt:
        type: int
        min: 0
        max: 100
    actions:
      close: {}
      open: {}
      setPosition:
        arguments:
          position:
            type: int
            required: true
      startPositionChange:
        arguments:
          direction: {}
      stopPositionChange: {}
      setTiltLevel:
        arguments:
          tilt:
            type: int
            required: true
  WindowShade:
    attributes:
      position:
        type: int
        min: 0
        max: 100
      windowShade:
        values: ["opening", "partially open", "closed", "open", "closing", "unknown"]
    actions:
      close: {}
      open: {}
      setPosition:
        arguments:
          position:
            type: int
            required: true
      startPositionChange:
        arguments:
          direction: {}
      stopPositionChange: {}
  ZwMultiChannel:
    attributes:
      epEvent: {}
      epInfo: {}
    actions:
      enableEpEvents:
        arguments:
          string: {}
      epCmd:
        arguments:
          number:
            type: int
          string: {}

extensions:
  - match_attribute: current
    capabilities: CurrentMeter#current
  - match_attribute: power
    capabilities: PowerMeter

# Because Hubitat does not give us any hints about device type, just a list of
# attributes and capabilities, attempt to guess a device type from what we've
# been given. The typemap (array) contains a list of matching rules in a binary
# tree. For each element, if the 'has' capabilities are present on the device,
# it follows that branch. If the next node has an "and", its "has" array is
# processed as its parent was. Where a match occurs and there is no "and",
# or the "and" fails, the "is" determines the type. See the Switch subtree
# for an example. Any type determined here can be overridden by the contents
# of `local_hubitat_devices.yaml`.
typemap:

  - has: Thermostat
    is: Thermostat:hvac_control.state

  - has: WindowShade
    is: Cover:cover.state

  - has: [MusicPlayer, MediaTransport]
    is: MediaPlayer:av_transport.state

  - has: Lock
    is: Lock:lock.state

  - has: Switch
    and:
      - has: SwitchLevel
        is: Light:dimming.level
    is: Switch:power_switch.state

  - has: Sensor
    and:
      - has: MotionSensor
        is: BinarySensor:motion_sensor.state

      - has: WaterSensor
        is: BinarySensor:leak_detector.state

      - has: ContactSensor
        is: BinarySensor:door_sensor.state

      - has: TemperatureMeasurement
        is: ValueSensor:temperature_sensor.value

      - has: RelativeHumidityMeasurement
        is: ValueSensor:humidity_sensor.value

      - has: PresenceSensor
        is: BinarySensor:presence_sensor.state

      - has: AirQualitySensor
        is: ValueSensor:air_quality_sensor.value

      - has: VoltageMeasurement
        is: ValueSensor:voltage_sensor.value

      - has: PowerMeter
        is: ValueSensor:power_sensor.value

      - has: CurrentMeter
        is: ValueSensor:current_meter.value

      - has: IlluminanceMeasurement
        is: ValueSensor:light_sensor.value

      - has: UltravioletIndex
        is: ValueSensor:uv_sensor.value

      - has: PressureMeasurement
        is: ValueSensor:pressure_sensor.value

      - has: Variable
        is: ValueSensor:string_sensor.value

      - has: [CarbonDioxideMeasurement, phMeasurement, SoundPressureLevel, ValueSensor]
        is: ValueSensor:value_sensor.value

      - has: [AcceletationSensor, ShockSensor, SleepSensor, SoundSensor, TouchSensor, Beacon]
        is: BinarySensor:binary_sensor.state

      - has: PushableButton
        is: Entity:x_hubitat_PushableButton.pushed
