End-user configurable, Time-Profile-based control (For Lights, Heating, and more)!

Design Goal:

I wanted to create a system which allows “Profiles” to control or provide input to various devices within my home, notably Lights and Heaters. These Profiles should:

  • Allow one-hour resolution for providing input on a daily schedule
  • Be user-adjustable and easy to understand via widgets in the UI, even to non-programmers
  • Accommodate multiple data types: Percentages, Temperatures, and Booleans
  • Be easy to set up and easy to implement wherever a profile might be needed
  • Utilize only standard rules DSL and items

I’ve made a similar system before, however due to changes with how widgets work, it wasn’t compatible with OH3 – some reworking was required.

Profile Control: Widgets

There are two main widgets that sit at the core of profile adjustment, and a popup for data entry. These are:

  • profile_looper: this allows the adjustment of profiles via sliders
  • profile_looper_popup: this allows users to configure profiles to their liking
  • profile_graph: this displays a graphical representation of a single profile

Basically, a user can first choose an unused profile (empty templates are easy to create via copy-paste) and then they can configure it using the profile_looper widget. This has a simple slider for each time period, and depending on what type it is set to, will allow the user to enter appropriate values. Additionally, by clicking on the gear-icon, profile_looper_popup is shown, which allows the user to do things like change the name, type, and interpolation setting (when active, this interpolates values between, useful for lighting).

profile_looper:

image

profile_looper_popup:

image

profile_graph:

image

Profile Control: Results

The Profile system results in a calculated value which sits in the profile’s accompanying _Calculated item and can, from there, be easily fetched by any other rule, light, thermostat, etc. Below are two examples of this calculated value: on the left, Profile 5 “Workday” is shown and on the right a light-brightness profile is shown which includes interpolation for smooth transitions throughout the day.

The profile calculator also calculates some extra values, like the minimmum and maximum values within a specific profile (which will be useful later) and has the ability to reset and revert the profiles to hard-coded values in the rules file should one wish to do so.

The biggest issue with this system that I’ve come across is that OpenHab gets a bit weak in the knees when opening multiple instances of the profile_looper widget, so much so that opening all 10 configurateable profiles usually “'breaks” the tab of the browser its in. Values can still be adjusted and everything works on that page, however the rest of mainUI in that tab is completly borked and to regain usability, I need to kill the tab and open mainUI again. This can be avoided by only viewing one profile at a time.

Also, it is important to include restoreOnStartup persistence for all profile items! Since many of the items that make this system work are not numbers, I use mapdb to keep all these values. If they are not persisted, your profiles will not survive a restart of OH.

In another post, I’ll show how I use these profiles to control lights and heating, and the accompanying widgets :smiley:

Profile Control: Source Code

YAML Widgets

profile_looper

uid: profile_looper
props:
  parameters:
    - description: Title on top
      label: Title
      name: title
      required: false
      type: TEXT
    - context: item
      description: Profile
      label: profile core item
      name: profilecore
      required: true
      type: TEXT
      filterCriteria:
        - value: Group
          name: type
  parameterGroups: []
timestamp: Dec 22, 2022, 4:26:06 PM
component: f7-card
config:
  style:
    --f7-range-bar-active-bg-color: "#090909"
    --f7-range-bar-bg-color: "#111111"
    --f7-range-bar-border-radius: 1em
    --f7-range-bar-size: 0.6em
    height: 400px
    position: relative
    width: 100%
  title: Profile Adjustment Widget
slots:
  default:
    - component: f7-block
      slots:
        default:
          - component: f7-row
            slots:
              default:
                - component: Label
                  config:
                    style:
                      font-size: 18pt
                      padding: 10px
                    text: =items[props.profilecore + "_Name"].state
                - component: oh-button
                  config:
                    action: popup
                    actionModal: widget:profile_looper_popup
                    actionModalConfig:
                      profile: =props.profilecore
                      title: =props.title
                    color: white
                    iconF7: gear_alt_fill
                    large: true
    - component: f7-block
      config:
        style:
          display: flex
          flex-direction: row
          height: 100%
          left: 30px
          top: 20px
          width: 95%
      slots:
        default:
          - component: oh-repeater
            config:
              filter: loop.item.type == "Number"
              for: item
              fragment: true
              groupItem: =props.profilecore + '_val'
              sourceType: itemsInGroup
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      height: 200px
                      width: 100%
                  slots:
                    default:
                      - component: f7-row
                        config:
                          visible: "=(items[props.profilecore + '_Type'].state == 'Percentage') ? true : false"
                        slots:
                          default:
                            - component: oh-slider
                              config:
                                item: =loop.item.name
                                label: true
                                max: 100
                                min: 0
                                scale: "=(loop.item.label == '00') ? true : false"
                                scaleSteps: 4
                                step: 1
                                style:
                                  --f7-range-bar-active-bg-color: "=(dayjs().format('HH') == loop.item.label) ? '#FFAF80' : '' "
                                  --f7-range-knob-color: "=(dayjs().format('HH') == loop.item.label) ? '#FF6700' : '' "
                                  height: 200px
                                title: =loop.item.label
                                vertical: 1
                      - component: f7-row
                        config:
                          visible: "=(items[props.profilecore + '_Type'].state == 'Temperature') ? true : false"
                        slots:
                          default:
                            - component: oh-slider
                              config:
                                item: =loop.item.name
                                label: true
                                max: 25
                                min: 5
                                releaseOnly: true
                                scale: "=(loop.item.label == '00') ? true : false"
                                scaleSteps: 5
                                step: 0.5
                                style:
                                  --f7-range-bar-active-bg-color: "=(dayjs().format('HH') == loop.item.label) ? '#FFAF80' : '' "
                                  --f7-range-knob-color: "=(dayjs().format('HH') == loop.item.label) ? '#FF6700' : '' "
                                  height: 200px
                                title: =loop.item.label
                                vertical: 1
                      - component: f7-row
                        config:
                          visible: "=(items[props.profilecore + '_Type'].state == 'Boolean') ? true : false"
                        slots:
                          default:
                            - component: oh-slider
                              config:
                                item: =loop.item.name
                                label: true
                                max: 1
                                min: 0
                                scale: "=(loop.item.label == '00') ? true : false"
                                scaleSteps: 1
                                step: 1
                                style:
                                  --f7-range-bar-active-bg-color: "=(dayjs().format('HH') == loop.item.label) ? '#FFAF80' : '' "
                                  --f7-range-knob-color: "=(dayjs().format('HH') == loop.item.label) ? '#FF6700' : '' "
                                  height: 200px
                                title: =loop.item.label
                                vertical: 1
                      - component: f7-row
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  position: relative
                                  top: 30px
                                  transform: rotate(-70deg)
                                  width: 1px
                                text: =loop.item.label + ":00"
                                visible: true
    - component: Label
      config:
        style:
          height: 1px
          left: 5px
          position: absolute
          top: 210px
          transform: rotate(-90deg)
          width: 1px
        text: =items[props.profilecore + "_Type"].state

profile_looper_popup

uid: profile_looper_popup
tags: []
props:
  parameters:
    - description: Title of the card
      label: Title
      name: title
      required: false
      type: TEXT
    - context: item
      description: Profile to manipulate
      label: Item
      name: profile
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Dec 22, 2022, 4:26:27 PM
component: f7-card
config:
  title: ="Profile settings of " + items[props.profile + "_Name"].state
slots:
  default:
    - {}
    - component: f7-card-content
      slots:
        default:
          - component: f7-row
            config:
              style:
                height: 3em
            slots:
              default:
                - component: f7-col
                  config:
                    width: 25
                  slots:
                    default:
                      - component: Label
                        config:
                          text: Profile Name
                - component: f7-col
                  config:
                    width: 75
                  slots:
                    default:
                      - component: oh-input
                        config:
                          item: =props.profile + '_Name'
                          name: Name
                          outline: true
                          placeholder: = items[props.profile + '_Name'].state
                          sendButton: true
                          type: text
          - component: f7-row
            slots:
              default:
                - component: f7-col
                  slots:
                    default:
                      - component: f7-segmented
                        slots:
                          default:
                            - component: oh-button
                              config:
                                action: command
                                actionCommand: Percentage
                                actionItem: =props.profile + "_Type"
                                active: "=(items[props.profile + '_Type'].state == 'Percentage') ? true : false"
                                outline: true
                                round: true
                                text: Percentage
                            - component: oh-button
                              config:
                                action: command
                                actionCommand: Temperature
                                actionItem: =props.profile + "_Type"
                                active: "=(items[props.profile + '_Type'].state == 'Temperature') ? true : false"
                                outline: true
                                round: true
                                text: Temperature
                            - component: oh-button
                              config:
                                action: command
                                actionCommand: Boolean
                                actionItem: =props.profile + "_Type"
                                active: "=(items[props.profile + '_Type'].state == 'Boolean') ? true : false"
                                outline: true
                                round: true
                                text: Boolean
          - component: f7-row
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      font-size: 12pt
                      height: 2em
                      margin-top: 0.5em
                      text-align: right
                  slots:
                    default:
                      - component: Label
                        config:
                          text: "Interpolation mode:"
                - component: f7-col
                  config:
                    style:
                      margin-top: 0.7em
                  slots:
                    default:
                      - component: oh-toggle
                        config:
                          item: =props.profile + "_Interpolate"
          - component: f7-row
            slots:
              default:
                - component: f7-col
                  config: {}
                  slots:
                    default:
                      - component: oh-button
                        config:
                          action: command
                          actionCommand: RESET
                          actionItem: =props.profile + '_Command'
                          large: true
                          outline: true
                          style:
                            margin-top: 1em
                          text: Reset Profile?

profile_graph

uid: profile_graph
tags: []
props:
  parameters:
    - context: item
      description: A Profile to graph
      label: Item
      name: profilecore
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Dec 22, 2022, 4:25:51 PM
component: f7-card
config:
  style:
    height: 18em
slots:
  default:
    - component: f7-block
      config:
        style:
          display: flex
          flex-direction: row
          height: 14em
          left: 1.5em
          position: relative
          width: 95%
      slots:
        default:
          - component: oh-repeater
            config:
              filter: loop.item.type == "Number"
              for: item
              fragment: true
              groupItem: =props.profilecore + '_val'
              sourceType: itemsInGroup
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      height: 14em
                      width: 100%
                  slots:
                    default:
                      - component: f7-row
                        config:
                          style:
                            bottom: 0px
                            height: =loop.item.state / 8 + 'em'
                            position: absolute
                            width: 4.11%
                          visible: "=(items[props.profilecore + '_Type'].state == 'Percentage') ? true : false"
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  background: "=( Number.parseInt(dayjs().format('HH')) > Number.parseInt(loop.item.label)) ? '#E5B710' : ( Number.parseInt(dayjs().format('HH')) == Number.parseInt(loop.item.label)) ? '#AC890C' : '#F2CB40' "
                                  color: black
                                  font-size: 7pt
                                  font-weight: bold
                                  height: 100%
                                  text-align: center
                                  width: 100%
                                text: =loop.item.state
                      - component: f7-row
                        config:
                          style:
                            bottom: 0px
                            height: =(loop.item.state - 13) * 1.3 + 'em'
                            position: absolute
                            width: 4.11%
                          visible: "=(items[props.profilecore + '_Type'].state == 'Temperature') ? true : false"
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  background: "=( Number.parseInt(dayjs().format('HH')) > Number.parseInt(loop.item.label)) ? '#C30F09' : ( Number.parseInt(dayjs().format('HH')) == Number.parseInt(loop.item.label)) ? '#FFFFFF' : '#F53029' "
                                  color: black
                                  font-size: 7pt
                                  font-weight: bold
                                  height: 100%
                                  text-align: center
                                  width: 100%
                                text: =loop.item.state
                      - component: f7-row
                        config:
                          style:
                            bottom: 0px
                            height: =(loop.item.state + 2) * 1 + 'em'
                            position: absolute
                            width: 4.11%
                          visible: "=(items[props.profilecore + '_Type'].state == 'Boolean') ? true : false"
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  background: "=( Number.parseInt(dayjs().format('HH')) > Number.parseInt(loop.item.label)) ? '#43A43D' : ( Number.parseInt(dayjs().format('HH')) == Number.parseInt(loop.item.label)) ? '#4CB944' : '#70C86A' "
                                  color: black
                                  font-size: 9pt
                                  font-weight: bold
                                  height: 100%
                                  text-align: center
                                  width: 100%
                                text: =loop.item.state
                      - component: f7-row
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  position: absolute
                                  top: 16em
                                  transform: rotate(-70deg)
                                  width: 1px
                                text: =loop.item.label + ":00"
                                visible: true
    - component: Label
      config:
        style:
          height: 1px
          left: 5px
          position: absolute
          top: 10em
          transform: rotate(-90deg)
          width: 1px
        text: =items[props.profilecore + "_Type"].state

Virtual Items

.items file template

//GENERAL VIRTUAL ITEMS
Group ProfileCMD "Profile command channel"
Group ProfileList "List of all available profiles"
Group ProfileType "Profile type command channel"
Group ProfileSlider "All Profile Sliders"

Switch ProfileMasterReset "Reset all profiles"

//BEGIN PROFILE BLOCK
Group Away (ProfileList)
Group Away_val (Away)

String Away_Name        (Away) 
String Away_Command     (Away, ProfileCMD) 
String Away_Type        (Away, ProfileType) 
Switch Away_Interpolate (Away) 
Number Away_Calculated  (Away)
Number Away_MaxValue    (Away)
Number Away_MinValue    (Away)

Number Away_0 "00" (Away_val, ProfileSlider) 
Number Away_1 "01" (Away_val, ProfileSlider)
Number Away_2 "02" (Away_val, ProfileSlider)
Number Away_3 "03" (Away_val, ProfileSlider)
Number Away_4 "04" (Away_val, ProfileSlider)
Number Away_5 "05" (Away_val, ProfileSlider)
Number Away_6 "06" (Away_val, ProfileSlider)
Number Away_7 "07" (Away_val, ProfileSlider)
Number Away_8 "08" (Away_val, ProfileSlider)
Number Away_9 "09" (Away_val, ProfileSlider)
Number Away_10 "10" (Away_val, ProfileSlider)
Number Away_11 "11" (Away_val, ProfileSlider)
Number Away_12 "12" (Away_val, ProfileSlider)
Number Away_13 "13" (Away_val, ProfileSlider)
Number Away_14 "14" (Away_val, ProfileSlider)
Number Away_15 "15" (Away_val, ProfileSlider)
Number Away_16 "16" (Away_val, ProfileSlider)
Number Away_17 "17" (Away_val, ProfileSlider)
Number Away_18 "18" (Away_val, ProfileSlider)
Number Away_19 "19" (Away_val, ProfileSlider)
Number Away_20 "20" (Away_val, ProfileSlider)
Number Away_21 "21" (Away_val, ProfileSlider)
Number Away_22 "22" (Away_val, ProfileSlider)
Number Away_23 "23" (Away_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Present (ProfileList)
Group Present_val (Present)

String Present_Name        (Present) 
String Present_Command     (Present, ProfileCMD) 
String Present_Type        (Present, ProfileType) 
Switch Present_Interpolate (Present) 
Number Present_Calculated  (Present)
Number Present_MaxValue    (Present)
Number Present_MinValue    (Present)

Number Present_0 "00" (Present_val, ProfileSlider) 
Number Present_1 "01" (Present_val, ProfileSlider)
Number Present_2 "02" (Present_val, ProfileSlider)
Number Present_3 "03" (Present_val, ProfileSlider)
Number Present_4 "04" (Present_val, ProfileSlider)
Number Present_5 "05" (Present_val, ProfileSlider)
Number Present_6 "06" (Present_val, ProfileSlider)
Number Present_7 "07" (Present_val, ProfileSlider)
Number Present_8 "08" (Present_val, ProfileSlider)
Number Present_9 "09" (Present_val, ProfileSlider)
Number Present_10 "10" (Present_val, ProfileSlider)
Number Present_11 "11" (Present_val, ProfileSlider)
Number Present_12 "12" (Present_val, ProfileSlider)
Number Present_13 "13" (Present_val, ProfileSlider)
Number Present_14 "14" (Present_val, ProfileSlider)
Number Present_15 "15" (Present_val, ProfileSlider)
Number Present_16 "16" (Present_val, ProfileSlider)
Number Present_17 "17" (Present_val, ProfileSlider)
Number Present_18 "18" (Present_val, ProfileSlider)
Number Present_19 "19" (Present_val, ProfileSlider)
Number Present_20 "20" (Present_val, ProfileSlider)
Number Present_21 "21" (Present_val, ProfileSlider)
Number Present_22 "22" (Present_val, ProfileSlider)
Number Present_23 "23" (Present_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Frostguard (ProfileList)
Group Frostguard_val (Frostguard)

String Frostguard_Name        (Frostguard) 
String Frostguard_Command     (Frostguard, ProfileCMD) 
String Frostguard_Type        (Frostguard, ProfileType) 
Switch Frostguard_Interpolate (Frostguard) 
Number Frostguard_Calculated  (Frostguard)
Number Frostguard_MaxValue    (Frostguard)
Number Frostguard_MinValue    (Frostguard)

Number Frostguard_0 "00" (Frostguard_val, ProfileSlider) 
Number Frostguard_1 "01" (Frostguard_val, ProfileSlider)
Number Frostguard_2 "02" (Frostguard_val, ProfileSlider)
Number Frostguard_3 "03" (Frostguard_val, ProfileSlider)
Number Frostguard_4 "04" (Frostguard_val, ProfileSlider)
Number Frostguard_5 "05" (Frostguard_val, ProfileSlider)
Number Frostguard_6 "06" (Frostguard_val, ProfileSlider)
Number Frostguard_7 "07" (Frostguard_val, ProfileSlider)
Number Frostguard_8 "08" (Frostguard_val, ProfileSlider)
Number Frostguard_9 "09" (Frostguard_val, ProfileSlider)
Number Frostguard_10 "10" (Frostguard_val, ProfileSlider)
Number Frostguard_11 "11" (Frostguard_val, ProfileSlider)
Number Frostguard_12 "12" (Frostguard_val, ProfileSlider)
Number Frostguard_13 "13" (Frostguard_val, ProfileSlider)
Number Frostguard_14 "14" (Frostguard_val, ProfileSlider)
Number Frostguard_15 "15" (Frostguard_val, ProfileSlider)
Number Frostguard_16 "16" (Frostguard_val, ProfileSlider)
Number Frostguard_17 "17" (Frostguard_val, ProfileSlider)
Number Frostguard_18 "18" (Frostguard_val, ProfileSlider)
Number Frostguard_19 "19" (Frostguard_val, ProfileSlider)
Number Frostguard_20 "20" (Frostguard_val, ProfileSlider)
Number Frostguard_21 "21" (Frostguard_val, ProfileSlider)
Number Frostguard_22 "22" (Frostguard_val, ProfileSlider)
Number Frostguard_23 "23" (Frostguard_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile1 (ProfileList)
Group Profile1_val (Profile1)

String Profile1_Name        (Profile1) 
String Profile1_Command     (Profile1, ProfileCMD) 
String Profile1_Type        (Profile1, ProfileType) 
Switch Profile1_Interpolate (Profile1) 
Number Profile1_Calculated  (Profile1)
Number Profile1_MaxValue    (Profile1)
Number Profile1_MinValue    (Profile1)

Number Profile1_0 "00" (Profile1_val, ProfileSlider) 
Number Profile1_1 "01" (Profile1_val, ProfileSlider)
Number Profile1_2 "02" (Profile1_val, ProfileSlider)
Number Profile1_3 "03" (Profile1_val, ProfileSlider)
Number Profile1_4 "04" (Profile1_val, ProfileSlider)
Number Profile1_5 "05" (Profile1_val, ProfileSlider)
Number Profile1_6 "06" (Profile1_val, ProfileSlider)
Number Profile1_7 "07" (Profile1_val, ProfileSlider)
Number Profile1_8 "08" (Profile1_val, ProfileSlider)
Number Profile1_9 "09" (Profile1_val, ProfileSlider)
Number Profile1_10 "10" (Profile1_val, ProfileSlider)
Number Profile1_11 "11" (Profile1_val, ProfileSlider)
Number Profile1_12 "12" (Profile1_val, ProfileSlider)
Number Profile1_13 "13" (Profile1_val, ProfileSlider)
Number Profile1_14 "14" (Profile1_val, ProfileSlider)
Number Profile1_15 "15" (Profile1_val, ProfileSlider)
Number Profile1_16 "16" (Profile1_val, ProfileSlider)
Number Profile1_17 "17" (Profile1_val, ProfileSlider)
Number Profile1_18 "18" (Profile1_val, ProfileSlider)
Number Profile1_19 "19" (Profile1_val, ProfileSlider)
Number Profile1_20 "20" (Profile1_val, ProfileSlider)
Number Profile1_21 "21" (Profile1_val, ProfileSlider)
Number Profile1_22 "22" (Profile1_val, ProfileSlider)
Number Profile1_23 "23" (Profile1_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile2 (ProfileList)
Group Profile2_val (Profile2)

String Profile2_Name        (Profile2) 
String Profile2_Command     (Profile2, ProfileCMD) 
String Profile2_Type        (Profile2, ProfileType) 
Switch Profile2_Interpolate (Profile2) 
Number Profile2_Calculated  (Profile2)
Number Profile2_MaxValue    (Profile2)
Number Profile2_MinValue    (Profile2)

Number Profile2_0 "00" (Profile2_val, ProfileSlider) 
Number Profile2_1 "01" (Profile2_val, ProfileSlider)
Number Profile2_2 "02" (Profile2_val, ProfileSlider)
Number Profile2_3 "03" (Profile2_val, ProfileSlider)
Number Profile2_4 "04" (Profile2_val, ProfileSlider)
Number Profile2_5 "05" (Profile2_val, ProfileSlider)
Number Profile2_6 "06" (Profile2_val, ProfileSlider)
Number Profile2_7 "07" (Profile2_val, ProfileSlider)
Number Profile2_8 "08" (Profile2_val, ProfileSlider)
Number Profile2_9 "09" (Profile2_val, ProfileSlider)
Number Profile2_10 "10" (Profile2_val, ProfileSlider)
Number Profile2_11 "11" (Profile2_val, ProfileSlider)
Number Profile2_12 "12" (Profile2_val, ProfileSlider)
Number Profile2_13 "13" (Profile2_val, ProfileSlider)
Number Profile2_14 "14" (Profile2_val, ProfileSlider)
Number Profile2_15 "15" (Profile2_val, ProfileSlider)
Number Profile2_16 "16" (Profile2_val, ProfileSlider)
Number Profile2_17 "17" (Profile2_val, ProfileSlider)
Number Profile2_18 "18" (Profile2_val, ProfileSlider)
Number Profile2_19 "19" (Profile2_val, ProfileSlider)
Number Profile2_20 "20" (Profile2_val, ProfileSlider)
Number Profile2_21 "21" (Profile2_val, ProfileSlider)
Number Profile2_22 "22" (Profile2_val, ProfileSlider)
Number Profile2_23 "23" (Profile2_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile3 (ProfileList)
Group Profile3_val (Profile3)

String Profile3_Name        (Profile3) 
String Profile3_Command     (Profile3, ProfileCMD) 
String Profile3_Type        (Profile3, ProfileType) 
Switch Profile3_Interpolate (Profile3) 
Number Profile3_Calculated  (Profile3)
Number Profile3_MaxValue    (Profile3)
Number Profile3_MinValue    (Profile3)

Number Profile3_0 "00" (Profile3_val, ProfileSlider) 
Number Profile3_1 "01" (Profile3_val, ProfileSlider)
Number Profile3_2 "02" (Profile3_val, ProfileSlider)
Number Profile3_3 "03" (Profile3_val, ProfileSlider)
Number Profile3_4 "04" (Profile3_val, ProfileSlider)
Number Profile3_5 "05" (Profile3_val, ProfileSlider)
Number Profile3_6 "06" (Profile3_val, ProfileSlider)
Number Profile3_7 "07" (Profile3_val, ProfileSlider)
Number Profile3_8 "08" (Profile3_val, ProfileSlider)
Number Profile3_9 "09" (Profile3_val, ProfileSlider)
Number Profile3_10 "10" (Profile3_val, ProfileSlider)
Number Profile3_11 "11" (Profile3_val, ProfileSlider)
Number Profile3_12 "12" (Profile3_val, ProfileSlider)
Number Profile3_13 "13" (Profile3_val, ProfileSlider)
Number Profile3_14 "14" (Profile3_val, ProfileSlider)
Number Profile3_15 "15" (Profile3_val, ProfileSlider)
Number Profile3_16 "16" (Profile3_val, ProfileSlider)
Number Profile3_17 "17" (Profile3_val, ProfileSlider)
Number Profile3_18 "18" (Profile3_val, ProfileSlider)
Number Profile3_19 "19" (Profile3_val, ProfileSlider)
Number Profile3_20 "20" (Profile3_val, ProfileSlider)
Number Profile3_21 "21" (Profile3_val, ProfileSlider)
Number Profile3_22 "22" (Profile3_val, ProfileSlider)
Number Profile3_23 "23" (Profile3_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile4 (ProfileList)
Group Profile4_val (Profile4)

String Profile4_Name        (Profile4) 
String Profile4_Command     (Profile4, ProfileCMD) 
String Profile4_Type        (Profile4, ProfileType) 
Switch Profile4_Interpolate (Profile4) 
Number Profile4_Calculated  (Profile4)
Number Profile4_MaxValue    (Profile4)
Number Profile4_MinValue    (Profile4)

Number Profile4_0 "00" (Profile4_val, ProfileSlider) 
Number Profile4_1 "01" (Profile4_val, ProfileSlider)
Number Profile4_2 "02" (Profile4_val, ProfileSlider)
Number Profile4_3 "03" (Profile4_val, ProfileSlider)
Number Profile4_4 "04" (Profile4_val, ProfileSlider)
Number Profile4_5 "05" (Profile4_val, ProfileSlider)
Number Profile4_6 "06" (Profile4_val, ProfileSlider)
Number Profile4_7 "07" (Profile4_val, ProfileSlider)
Number Profile4_8 "08" (Profile4_val, ProfileSlider)
Number Profile4_9 "09" (Profile4_val, ProfileSlider)
Number Profile4_10 "10" (Profile4_val, ProfileSlider)
Number Profile4_11 "11" (Profile4_val, ProfileSlider)
Number Profile4_12 "12" (Profile4_val, ProfileSlider)
Number Profile4_13 "13" (Profile4_val, ProfileSlider)
Number Profile4_14 "14" (Profile4_val, ProfileSlider)
Number Profile4_15 "15" (Profile4_val, ProfileSlider)
Number Profile4_16 "16" (Profile4_val, ProfileSlider)
Number Profile4_17 "17" (Profile4_val, ProfileSlider)
Number Profile4_18 "18" (Profile4_val, ProfileSlider)
Number Profile4_19 "19" (Profile4_val, ProfileSlider)
Number Profile4_20 "20" (Profile4_val, ProfileSlider)
Number Profile4_21 "21" (Profile4_val, ProfileSlider)
Number Profile4_22 "22" (Profile4_val, ProfileSlider)
Number Profile4_23 "23" (Profile4_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile5 (ProfileList)
Group Profile5_val (Profile5)

String Profile5_Name        (Profile5) 
String Profile5_Command     (Profile5, ProfileCMD) 
String Profile5_Type        (Profile5, ProfileType) 
Switch Profile5_Interpolate (Profile5) 
Number Profile5_Calculated  (Profile5)
Number Profile5_MaxValue    (Profile5)
Number Profile5_MinValue    (Profile5)

Number Profile5_0 "00" (Profile5_val, ProfileSlider) 
Number Profile5_1 "01" (Profile5_val, ProfileSlider)
Number Profile5_2 "02" (Profile5_val, ProfileSlider)
Number Profile5_3 "03" (Profile5_val, ProfileSlider)
Number Profile5_4 "04" (Profile5_val, ProfileSlider)
Number Profile5_5 "05" (Profile5_val, ProfileSlider)
Number Profile5_6 "06" (Profile5_val, ProfileSlider)
Number Profile5_7 "07" (Profile5_val, ProfileSlider)
Number Profile5_8 "08" (Profile5_val, ProfileSlider)
Number Profile5_9 "09" (Profile5_val, ProfileSlider)
Number Profile5_10 "10" (Profile5_val, ProfileSlider)
Number Profile5_11 "11" (Profile5_val, ProfileSlider)
Number Profile5_12 "12" (Profile5_val, ProfileSlider)
Number Profile5_13 "13" (Profile5_val, ProfileSlider)
Number Profile5_14 "14" (Profile5_val, ProfileSlider)
Number Profile5_15 "15" (Profile5_val, ProfileSlider)
Number Profile5_16 "16" (Profile5_val, ProfileSlider)
Number Profile5_17 "17" (Profile5_val, ProfileSlider)
Number Profile5_18 "18" (Profile5_val, ProfileSlider)
Number Profile5_19 "19" (Profile5_val, ProfileSlider)
Number Profile5_20 "20" (Profile5_val, ProfileSlider)
Number Profile5_21 "21" (Profile5_val, ProfileSlider)
Number Profile5_22 "22" (Profile5_val, ProfileSlider)
Number Profile5_23 "23" (Profile5_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile6 (ProfileList)
Group Profile6_val (Profile6)

String Profile6_Name        (Profile6) 
String Profile6_Command     (Profile6, ProfileCMD) 
String Profile6_Type        (Profile6, ProfileType) 
Switch Profile6_Interpolate (Profile6) 
Number Profile6_Calculated  (Profile6)
Number Profile6_MaxValue    (Profile6)
Number Profile6_MinValue    (Profile6)

Number Profile6_0 "00" (Profile6_val, ProfileSlider) 
Number Profile6_1 "01" (Profile6_val, ProfileSlider)
Number Profile6_2 "02" (Profile6_val, ProfileSlider)
Number Profile6_3 "03" (Profile6_val, ProfileSlider)
Number Profile6_4 "04" (Profile6_val, ProfileSlider)
Number Profile6_5 "05" (Profile6_val, ProfileSlider)
Number Profile6_6 "06" (Profile6_val, ProfileSlider)
Number Profile6_7 "07" (Profile6_val, ProfileSlider)
Number Profile6_8 "08" (Profile6_val, ProfileSlider)
Number Profile6_9 "09" (Profile6_val, ProfileSlider)
Number Profile6_10 "10" (Profile6_val, ProfileSlider)
Number Profile6_11 "11" (Profile6_val, ProfileSlider)
Number Profile6_12 "12" (Profile6_val, ProfileSlider)
Number Profile6_13 "13" (Profile6_val, ProfileSlider)
Number Profile6_14 "14" (Profile6_val, ProfileSlider)
Number Profile6_15 "15" (Profile6_val, ProfileSlider)
Number Profile6_16 "16" (Profile6_val, ProfileSlider)
Number Profile6_17 "17" (Profile6_val, ProfileSlider)
Number Profile6_18 "18" (Profile6_val, ProfileSlider)
Number Profile6_19 "19" (Profile6_val, ProfileSlider)
Number Profile6_20 "20" (Profile6_val, ProfileSlider)
Number Profile6_21 "21" (Profile6_val, ProfileSlider)
Number Profile6_22 "22" (Profile6_val, ProfileSlider)
Number Profile6_23 "23" (Profile6_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile7 (ProfileList)
Group Profile7_val (Profile7)

String Profile7_Name        (Profile7) 
String Profile7_Command     (Profile7, ProfileCMD) 
String Profile7_Type        (Profile7, ProfileType) 
Switch Profile7_Interpolate (Profile7) 
Number Profile7_Calculated  (Profile7)
Number Profile7_MaxValue    (Profile7)
Number Profile7_MinValue    (Profile7)

Number Profile7_0 "00" (Profile7_val, ProfileSlider) 
Number Profile7_1 "01" (Profile7_val, ProfileSlider)
Number Profile7_2 "02" (Profile7_val, ProfileSlider)
Number Profile7_3 "03" (Profile7_val, ProfileSlider)
Number Profile7_4 "04" (Profile7_val, ProfileSlider)
Number Profile7_5 "05" (Profile7_val, ProfileSlider)
Number Profile7_6 "06" (Profile7_val, ProfileSlider)
Number Profile7_7 "07" (Profile7_val, ProfileSlider)
Number Profile7_8 "08" (Profile7_val, ProfileSlider)
Number Profile7_9 "09" (Profile7_val, ProfileSlider)
Number Profile7_10 "10" (Profile7_val, ProfileSlider)
Number Profile7_11 "11" (Profile7_val, ProfileSlider)
Number Profile7_12 "12" (Profile7_val, ProfileSlider)
Number Profile7_13 "13" (Profile7_val, ProfileSlider)
Number Profile7_14 "14" (Profile7_val, ProfileSlider)
Number Profile7_15 "15" (Profile7_val, ProfileSlider)
Number Profile7_16 "16" (Profile7_val, ProfileSlider)
Number Profile7_17 "17" (Profile7_val, ProfileSlider)
Number Profile7_18 "18" (Profile7_val, ProfileSlider)
Number Profile7_19 "19" (Profile7_val, ProfileSlider)
Number Profile7_20 "20" (Profile7_val, ProfileSlider)
Number Profile7_21 "21" (Profile7_val, ProfileSlider)
Number Profile7_22 "22" (Profile7_val, ProfileSlider)
Number Profile7_23 "23" (Profile7_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile8 (ProfileList)
Group Profile8_val (Profile8)

String Profile8_Name        (Profile8) 
String Profile8_Command     (Profile8, ProfileCMD) 
String Profile8_Type        (Profile8, ProfileType) 
Switch Profile8_Interpolate (Profile8) 
Number Profile8_Calculated  (Profile8)
Number Profile8_MaxValue    (Profile8)
Number Profile8_MinValue    (Profile8)

Number Profile8_0 "00" (Profile8_val, ProfileSlider) 
Number Profile8_1 "01" (Profile8_val, ProfileSlider)
Number Profile8_2 "02" (Profile8_val, ProfileSlider)
Number Profile8_3 "03" (Profile8_val, ProfileSlider)
Number Profile8_4 "04" (Profile8_val, ProfileSlider)
Number Profile8_5 "05" (Profile8_val, ProfileSlider)
Number Profile8_6 "06" (Profile8_val, ProfileSlider)
Number Profile8_7 "07" (Profile8_val, ProfileSlider)
Number Profile8_8 "08" (Profile8_val, ProfileSlider)
Number Profile8_9 "09" (Profile8_val, ProfileSlider)
Number Profile8_10 "10" (Profile8_val, ProfileSlider)
Number Profile8_11 "11" (Profile8_val, ProfileSlider)
Number Profile8_12 "12" (Profile8_val, ProfileSlider)
Number Profile8_13 "13" (Profile8_val, ProfileSlider)
Number Profile8_14 "14" (Profile8_val, ProfileSlider)
Number Profile8_15 "15" (Profile8_val, ProfileSlider)
Number Profile8_16 "16" (Profile8_val, ProfileSlider)
Number Profile8_17 "17" (Profile8_val, ProfileSlider)
Number Profile8_18 "18" (Profile8_val, ProfileSlider)
Number Profile8_19 "19" (Profile8_val, ProfileSlider)
Number Profile8_20 "20" (Profile8_val, ProfileSlider)
Number Profile8_21 "21" (Profile8_val, ProfileSlider)
Number Profile8_22 "22" (Profile8_val, ProfileSlider)
Number Profile8_23 "23" (Profile8_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile9 (ProfileList)
Group Profile9_val (Profile9)

String Profile9_Name        (Profile9) 
String Profile9_Command     (Profile9, ProfileCMD) 
String Profile9_Type        (Profile9, ProfileType) 
Switch Profile9_Interpolate (Profile9) 
Number Profile9_Calculated  (Profile9)
Number Profile9_MaxValue    (Profile9)
Number Profile9_MinValue    (Profile9)

Number Profile9_0 "00" (Profile9_val, ProfileSlider) 
Number Profile9_1 "01" (Profile9_val, ProfileSlider)
Number Profile9_2 "02" (Profile9_val, ProfileSlider)
Number Profile9_3 "03" (Profile9_val, ProfileSlider)
Number Profile9_4 "04" (Profile9_val, ProfileSlider)
Number Profile9_5 "05" (Profile9_val, ProfileSlider)
Number Profile9_6 "06" (Profile9_val, ProfileSlider)
Number Profile9_7 "07" (Profile9_val, ProfileSlider)
Number Profile9_8 "08" (Profile9_val, ProfileSlider)
Number Profile9_9 "09" (Profile9_val, ProfileSlider)
Number Profile9_10 "10" (Profile9_val, ProfileSlider)
Number Profile9_11 "11" (Profile9_val, ProfileSlider)
Number Profile9_12 "12" (Profile9_val, ProfileSlider)
Number Profile9_13 "13" (Profile9_val, ProfileSlider)
Number Profile9_14 "14" (Profile9_val, ProfileSlider)
Number Profile9_15 "15" (Profile9_val, ProfileSlider)
Number Profile9_16 "16" (Profile9_val, ProfileSlider)
Number Profile9_17 "17" (Profile9_val, ProfileSlider)
Number Profile9_18 "18" (Profile9_val, ProfileSlider)
Number Profile9_19 "19" (Profile9_val, ProfileSlider)
Number Profile9_20 "20" (Profile9_val, ProfileSlider)
Number Profile9_21 "21" (Profile9_val, ProfileSlider)
Number Profile9_22 "22" (Profile9_val, ProfileSlider)
Number Profile9_23 "23" (Profile9_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group Profile10 (ProfileList)
Group Profile10_val (Profile10)

String Profile10_Name        (Profile10) 
String Profile10_Command     (Profile10, ProfileCMD) 
String Profile10_Type        (Profile10, ProfileType) 
Switch Profile10_Interpolate (Profile10) 
Number Profile10_Calculated  (Profile10)
Number Profile10_MaxValue    (Profile10)
Number Profile10_MinValue    (Profile10)

Number Profile10_0 "00" (Profile10_val, ProfileSlider) 
Number Profile10_1 "01" (Profile10_val, ProfileSlider)
Number Profile10_2 "02" (Profile10_val, ProfileSlider)
Number Profile10_3 "03" (Profile10_val, ProfileSlider)
Number Profile10_4 "04" (Profile10_val, ProfileSlider)
Number Profile10_5 "05" (Profile10_val, ProfileSlider)
Number Profile10_6 "06" (Profile10_val, ProfileSlider)
Number Profile10_7 "07" (Profile10_val, ProfileSlider)
Number Profile10_8 "08" (Profile10_val, ProfileSlider)
Number Profile10_9 "09" (Profile10_val, ProfileSlider)
Number Profile10_10 "10" (Profile10_val, ProfileSlider)
Number Profile10_11 "11" (Profile10_val, ProfileSlider)
Number Profile10_12 "12" (Profile10_val, ProfileSlider)
Number Profile10_13 "13" (Profile10_val, ProfileSlider)
Number Profile10_14 "14" (Profile10_val, ProfileSlider)
Number Profile10_15 "15" (Profile10_val, ProfileSlider)
Number Profile10_16 "16" (Profile10_val, ProfileSlider)
Number Profile10_17 "17" (Profile10_val, ProfileSlider)
Number Profile10_18 "18" (Profile10_val, ProfileSlider)
Number Profile10_19 "19" (Profile10_val, ProfileSlider)
Number Profile10_20 "20" (Profile10_val, ProfileSlider)
Number Profile10_21 "21" (Profile10_val, ProfileSlider)
Number Profile10_22 "22" (Profile10_val, ProfileSlider)
Number Profile10_23 "23" (Profile10_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group AlwaysON (ProfileList)
Group AlwaysON_val (AlwaysON)

String AlwaysON_Name        (AlwaysON) 
String AlwaysON_Command     (AlwaysON, ProfileCMD) 
String AlwaysON_Type        (AlwaysON, ProfileType) 
Switch AlwaysON_Interpolate (AlwaysON) 
Number AlwaysON_Calculated  (AlwaysON)
Number AlwaysON_MaxValue    (AlwaysON)
Number AlwaysON_MinValue    (AlwaysON)

Number AlwaysON_0 "00" (AlwaysON_val, ProfileSlider) 
Number AlwaysON_1 "01" (AlwaysON_val, ProfileSlider)
Number AlwaysON_2 "02" (AlwaysON_val, ProfileSlider)
Number AlwaysON_3 "03" (AlwaysON_val, ProfileSlider)
Number AlwaysON_4 "04" (AlwaysON_val, ProfileSlider)
Number AlwaysON_5 "05" (AlwaysON_val, ProfileSlider)
Number AlwaysON_6 "06" (AlwaysON_val, ProfileSlider)
Number AlwaysON_7 "07" (AlwaysON_val, ProfileSlider)
Number AlwaysON_8 "08" (AlwaysON_val, ProfileSlider)
Number AlwaysON_9 "09" (AlwaysON_val, ProfileSlider)
Number AlwaysON_10 "10" (AlwaysON_val, ProfileSlider)
Number AlwaysON_11 "11" (AlwaysON_val, ProfileSlider)
Number AlwaysON_12 "12" (AlwaysON_val, ProfileSlider)
Number AlwaysON_13 "13" (AlwaysON_val, ProfileSlider)
Number AlwaysON_14 "14" (AlwaysON_val, ProfileSlider)
Number AlwaysON_15 "15" (AlwaysON_val, ProfileSlider)
Number AlwaysON_16 "16" (AlwaysON_val, ProfileSlider)
Number AlwaysON_17 "17" (AlwaysON_val, ProfileSlider)
Number AlwaysON_18 "18" (AlwaysON_val, ProfileSlider)
Number AlwaysON_19 "19" (AlwaysON_val, ProfileSlider)
Number AlwaysON_20 "20" (AlwaysON_val, ProfileSlider)
Number AlwaysON_21 "21" (AlwaysON_val, ProfileSlider)
Number AlwaysON_22 "22" (AlwaysON_val, ProfileSlider)
Number AlwaysON_23 "23" (AlwaysON_val, ProfileSlider)

//BEGIN PROFILE BLOCK
Group AlwaysOFF (ProfileList)
Group AlwaysOFF_val (AlwaysOFF)

String AlwaysOFF_Name        (AlwaysOFF) 
String AlwaysOFF_Command     (AlwaysOFF, ProfileCMD) 
String AlwaysOFF_Type        (AlwaysOFF, ProfileType) 
Switch AlwaysOFF_Interpolate (AlwaysOFF) 
Number AlwaysOFF_Calculated  (AlwaysOFF)
Number AlwaysOFF_MaxValue    (AlwaysOFF)
Number AlwaysOFF_MinValue    (AlwaysOFF)

Number AlwaysOFF_0 "00" (AlwaysOFF_val, ProfileSlider) 
Number AlwaysOFF_1 "01" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_2 "02" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_3 "03" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_4 "04" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_5 "05" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_6 "06" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_7 "07" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_8 "08" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_9 "09" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_10 "10" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_11 "11" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_12 "12" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_13 "13" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_14 "14" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_15 "15" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_16 "16" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_17 "17" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_18 "18" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_19 "19" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_20 "20" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_21 "21" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_22 "22" (AlwaysOFF_val, ProfileSlider)
Number AlwaysOFF_23 "23" (AlwaysOFF_val, ProfileSlider)
Rules code

Rules required

import java.util.List
import org.openhab.core.model.script.ScriptServiceUtil
//This file manages the profiles. It enables the control via the widgets, but also does the actual interpolation and places the values into the appropriate place. 

//Rule for initializing profile
rule "Profile Calculator - Interpret command - RESET"
when
    Member of ProfileCMD received command "RESET"
then
    triggeringItem.postUpdate("IDLE")
    var root_profile_groupname = triggeringItem.name.split('_').get(0)
    var root_group = ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname) as GroupItem
    var numb_group = ScriptServiceUtil.getItemRegistry.getItem(root_group.name + "_val") as GroupItem

    //Reset all the sliders
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname + "_" + c ).postUpdate(0)
    } 

    //Rest the main variables
    ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname + "_Name").postUpdate("" + root_profile_groupname)
    ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname + "_Type").postUpdate("Percentage")
    ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname + "_Interpolate").postUpdate(OFF)
end

//Rule for making sure values fit on sliders
rule "Profile Calculator - Adjust type - Fit values"
when
    Member of ProfileType received update or
    Member of ProfileSlider received command 
then
    var root_profile_groupname = triggeringItem.name.split('_').get(0)
    var root_group1 = ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname) as GroupItem
    var root_group = ScriptServiceUtil.getItemRegistry.getItem(root_group1.name + "_val") as GroupItem

    var foundMaxValue = 0
    var foundMinValue = 100

    //Check the ranges on all the sliders
    for(var c = 0; c < 24; c = c + 1 ){
        var slideritem = ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname + "_" + c) as GenericItem

        if(triggeringItem.state.toString == "Percentage"){
            if(slideritem.state > 100 || slideritem.state < 0 ){
                slideritem.sendCommand(0)
            }
        }
        if(triggeringItem.state.toString == "Temperature"){
            if(slideritem.state > 25 || slideritem.state < 5 ){
                slideritem.sendCommand(5)
            }
        }
        if(triggeringItem.state.toString == "Boolean"){
            if(slideritem.state > 1 || slideritem.state < 0 ){
                slideritem.sendCommand(0)
            }
        }

        if(slideritem.state > foundMaxValue){
            foundMaxValue = slideritem.state
        }
        if(slideritem.state < foundMinValue){
            foundMinValue = slideritem.state
        }

    } 
    logInfo("Profile Calculator","Min value: " + foundMinValue)
    logInfo("Profile Calculator","Max value: " + foundMaxValue)
    ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname + "_MinValue").postUpdate(foundMinValue)
    ScriptServiceUtil.getItemRegistry.getItem(root_profile_groupname + "_MaxValue").postUpdate(foundMaxValue)

end

//Rule for updating the values in all the profiles
rule "Profile Calculator - Calculate current value"
when
    Time cron "0 0/1 * * * ?"
then

    if(SystemReady.state != ON){
        logError("System Startup Catch", "Rule execution blocked -- Persistent variables not yet loaded!")
    }else{

        //Get the current time and put into two handy variables
        var curn_interval = (now.getHour()).intValue
        var next_interval = curn_interval + 1
        if(next_interval == 24) { next_interval = 0}

        var profilecounter = 0

        //logInfo("Profile Calculator", "Running Profile update cycle, it is currently interval " + curn_interval + " and the next interval is " + next_interval + ".")

        ProfileList.members.forEach[ root_profile |
            
            //logInfo("Profile Calculator", "Updating " + root_profile.name)

            profilecounter = profilecounter + 1
            
            var group_item = root_profile as GroupItem
            //check to see if it exists, if not, generate it!
            if(ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_Name").state == NULL){
                ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_Command").sendCommand("RESET")
                //Wait for it to generate
                Thread::sleep(1000)
            }

            //Check for interpolation
            if(ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_Interpolate").state == OFF){
                //No interpolation. Direct transfer OK
                var slidergroup = ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_val") as GroupItem
                var slideritem  = ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_" + curn_interval ) as NumberItem
                var targetitem  = ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_Calculated") as NumberItem

                targetitem.postUpdate( slideritem.state as Number)
                
            }else{
                //Interpolation required. Do some math first. 
                var slidergroup     = ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_val") as GroupItem
                var slideritem_now  = ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_" + curn_interval ) as NumberItem
                var slideritem_nxt  = ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_" + next_interval ) as NumberItem
                var targetitem      = ScriptServiceUtil.getItemRegistry.getItem(group_item.name + "_Calculated") as NumberItem

                var Number interpolatedvalue = slideritem_now.state as Number + ((now.getMinute() / 60.0 ) * (slideritem_nxt.state as Number - slideritem_now.state as Number))
                targetitem.postUpdate( interpolatedvalue.intValue )
            }
            
        ]

        logInfo("Profile Calculator", "Successfully Updated " + profilecounter + " profiles!")

    }
end

rule "Profile Calculator - load defaults"
when
    Item ProfileMasterReset received command ON
then
    ProfileMasterReset.sendCommand(OFF)

    //Light profiles
    Profile1_Name.sendCommand("Standard Light Brightness")
    Profile2_Name.sendCommand("Standard Light Temperature")
    Profile3_Name.sendCommand("Dimmer Light Brightness")
    Profile4_Name.sendCommand("Lower Light Brightness")
    Profile1_Interpolate.sendCommand(ON)
    Profile2_Interpolate.sendCommand(ON)
    Profile3_Interpolate.sendCommand(ON)
    Profile4_Interpolate.sendCommand(ON)
    Profile1_Type.sendCommand("Percentage")
    Profile2_Type.sendCommand("Percentage")
    Profile3_Type.sendCommand("Percentage")
    Profile4_Type.sendCommand("Percentage")
    //valuearrays
    var List<Integer> profile1values = newArrayList(12,13,13,13,14,15,41,61,91,99,100,99,97,96,94,92,89,85,74,57,40,30,20,15)
    var List<Integer> profile2values = newArrayList(0,0,0,0,0,12,29,56,74,83,88,89,89,85,78,70,63,53,39,26,18,10,0,0)
    var List<Integer> profile3values = newArrayList(6,3,4,6,7,9,19,22,32,30,29,28,23,9,8,8,9,11,18,21,22,19,11,10,7)
    var List<Integer> profile4values = newArrayList(8,9,9,9,9,9,13,28,36,62,81,85,86,87,89,85,81,71,64,50,36,19,11,7)

    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile1_" + c ).postUpdate(profile1values.get(c))
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile2_" + c ).postUpdate(profile2values.get(c))
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile3_" + c ).postUpdate(profile3values.get(c))
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile4_" + c ).postUpdate(profile4values.get(c))
    }

    //Thermo Profiles
    Profile5_Name.sendCommand("Workday")
    Profile6_Name.sendCommand("Weekend")
    Profile7_Name.sendCommand("Hot Morning")
    Profile8_Name.sendCommand("Alternate Heat")
    Profile5_Interpolate.sendCommand(OFF)
    Profile6_Interpolate.sendCommand(OFF)
    Profile7_Interpolate.sendCommand(OFF)
    Profile8_Interpolate.sendCommand(OFF)
    Profile5_Type.sendCommand("Temperature")
    Profile6_Type.sendCommand("Temperature")
    Profile7_Type.sendCommand("Temperature")
    Profile8_Type.sendCommand("Temperature")
    var List<Integer> profile5values = newArrayList(16,16,16,16,16,18,20,21,20,20,20,20,20,20,20,20,20,20,20,21,22,21,18,17)
    var List<Integer> profile6values = newArrayList(16,16,16,16,16,18,20,20,20,18,18,18,18,18,18,18,19,20,21,21,21,18,18,16)

    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile5_" + c ).postUpdate(profile5values.get(c))
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile6_" + c ).postUpdate(profile6values.get(c))
    }

    Away_Name.sendCommand("Away")
    Present_Name.sendCommand("Present")
    Frostguard_Name.sendCommand("Frostguard")
    Away_Interpolate.sendCommand(OFF)
    Present_Interpolate.sendCommand(OFF)
    Frostguard_Interpolate.sendCommand(OFF)
    Away_Type.sendCommand("Temperature")
    Present_Type.sendCommand("Temperature")
    Frostguard_Type.sendCommand("Temperature")

    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Away_" + c ).postUpdate(15)
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Present_" + c ).postUpdate(21)
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Frostguard_" + c ).postUpdate(12)
    }
    

    //Boolean Profiles
    Profile9_Name.sendCommand("Floorheating 1")
    Profile10_Name.sendCommand("Floorheating 2")
    AlwaysON_Name.sendCommand("Always ON")
    AlwaysOFF_Name.sendCommand("Always OFF")
    Profile9_Interpolate.sendCommand(OFF)
    Profile10_Interpolate.sendCommand(OFF)
    AlwaysON_Interpolate.sendCommand(OFF)
    AlwaysOFF_Interpolate.sendCommand(OFF)
    Profile9_Type.sendCommand("Boolean")
    Profile10_Type.sendCommand("Boolean")
    AlwaysON_Type.sendCommand("Boolean")
    AlwaysOFF_Type.sendCommand("Boolean")
    var List<Integer> profile9values = newArrayList(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0)
    var List<Integer> profile10values = newArrayList(0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0)
    var List<Integer> AlwaysOFFvalues = newArrayList(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
    var List<Integer> AlwaysONvalues = newArrayList(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)

    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile9_" + c ).postUpdate(profile9values.get(c))
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "Profile10_" + c ).postUpdate(profile10values.get(c))
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "AlwaysON_" + c ).postUpdate(AlwaysONvalues.get(c))
    }
    for(var c = 0; c < 24; c = c + 1 ){
        ScriptServiceUtil.getItemRegistry.getItem( "AlwaysOFF_" + c ).postUpdate(AlwaysOFFvalues.get(c))
    }


end
1 Like