Thermostat

 

Description

This model will show the actual temperature and set point of a vav.  It will change colors based off if it is heating or cooling, and will show occ or unocc.  The gear will pull up the actions, and the setpoint can be changed with the plus and minus buttons.

Completed Model (Basic)

Download: Thermostat.zip

Video

Example: 


Ractive Code

Template

<div class="s-topImg">
    <img class="s-img" src="/finGetFile/demo?fileRef=@20ba1036-2c3f2e99">
    <label class="view-superman-label s-imgTextB">{{myTemp.tz}}</label>
    <label class="view-superman-label s-imgTextW">{{myTemp.tz}}</label>
</div>
<div class="view-superman-label s-nameLabel">
    <label class="s-equipName">{{myTemp.equipRefDis}}</label>
</div>
<div class="s-actual {{#if backColor.navName}}Heating{{else}}Cooling{{/if}}">
    
        {{#myTemp}}
        <div>
        <label class="icon-checkmark {{#if checkMark.writeVal}}s-checkTrue{{else}}s-checkFalse{{/if}}"></label>
        <label class="view-superman-label s-checkLabel">{{#if checkMark.writeVal}}Occ{{else}}Unocc{{/if}}</label>
        <label class="view-superman-label s-unit">{{unit}}</label></div>
        <div align="center"><br><br>
        <label class="view-superman-label s-titleA">Actual Room Temperature</label><br>
        <label class="view-superman-label s-tempA">&nbsp {{curVal.toFixed(1)}}º &nbsp;</label><br>
        </div>
        <div><label class="{{#if checkMark.writeVal}}wi-day-sunny{{else}}wi-night-clear{{/if}} s-sun"></label>
        <label class="s-fire {{#if backColor.navName}}wi-fire{{else}}wi-snowflake-cold{{/if}}"></label></div>
        {{/myTemp}}
    
</div>
<div class="s-barDiv">
    <div class="view-superman-label s-bar {{#if backColor.navName}}Heating1{{else}}Cooling1{{/if}}" style="width: {{#if myTemp.maxVal}}{{myTemp.curVal/myTemp.maxVal*100}}{{else}}{{myTemp.curVal}}{{/if}}%;"></div>
    <div class="view-superman-label s-arrowA" style="left: {{#if myTemp.maxVal}}{{mySetPoint.curVal/ myTemp.maxVal*100}}{{else}}{{mySetPoint.curVal}}{{/if}}%;"></div>
    <div class="view-superman-label s-arrowSP" style="left: {{#if myTemp.maxVal}}{{myTemp.curVal/myTemp.maxVal*100}}{{else}}{{myTemp.curVal}}{{/if}}%;"></div>
</div>
<div class="s-setP"> 
  <div align="center">
  {{#mySetPoint}}
  <div class="icon-gear s-gear" on-click="actions"></div>
  <div class="view-superman-label s-circle"><div class="s-circleValue">{{writeLevel}}</div></div>
  <label class="view-superman-label s-circleLabel">Priority</label>
  <br><br>
  <label class="view-superman-label s-titleSP">Room Temperature Setpoint</label><br>
  <br>
    <div class="s-setDiv" align="center">
        <button class="view-superman-label s-button" on-click="decrement">
            <div class="icon-minus s-minus"></div>
        </button>
        <label class="view-superman-label s-tempSP">&nbsp;{{curVal}}º&nbsp;</label>
        <button class="view-superman-label s-button" on-click="increment">
            <div class="icon-plus s-plus"></div>
        </button>
    </div>
  {{/mySetPoint}}
  </div>
</div>

This sets up the model

Model

{
    data:
    {
        myTemp: null,
            mySetPoint: null,
            backColor: null,
            checkMark: null
    }
}


Style

.s-topImg{
    height: 20%;
    background-color: #e0e0e0;
}
.s-img{
    width: 100%;
    height: 100%;
}
.s-imgTextW{
    position:fixed;
    right:5%;
    bottom:85%;
    font-size: 400%;
    color: #fff;
}
.s-imgTextB{
    position:fixed;
    right:5.25%;
    bottom:84.75%;
    font-size: 400%;
    color: #000;
}
.s-nameLabel{
    background-color: #333;
    color: #fff;
    text-align: center;
    font-size: 25px;
    height: 10%;
    padding-top: 15px;
}
.s-arrowLeft{
    float: left;
    margin-top: -3px;
    color: #fff;
    font-size: 350%;
    cursor: pointer;
}
.s-arrowRight{
    float: right;
    margin-top: -3px;
    color: #fff;
    font-size: 350%;
    cursor: pointer;
}
.s-equipName{
    margin-top: 15px;
    font-size: 225%;
}
.s-actual{
    padding: 20px;
    height: 32%;  
}
.s-checkTrue{
    font-size: 3em;
    color: #6cc487;
    text-align: left;
}
.s-checkFalse{
    font-size: 3em;
    color: #afafb0;
    opacity: 0.5;
    text-align: left;
}
.s-checkLabel{
    color: #fff;
    position:fixed;
    right:92.4%;
    bottom:62.5%;
}
.s-unit{
    font-size: 3em;
    color: #fff;
    float: right;
}
.s-titleA{
    color: #fff;
    font-size: 350%;
}
.s-tempA{
    font-size: 1000%;
    color:#fff;
}
.s-sun{
    font-size: 3em;
    color: #fff;
    /*float: right;*/
    position:fixed;
    right:92%;
    bottom:39%;
}
.s-fire{
    font-size: 3em;
    color: #fff;
    position:fixed;
    right:3%;
    bottom:39%;
}
.s-barDiv{
    height: 5%;
    background: #CFD8DC;
    margin: 0 auto;
    position: relative;
}
.s-bar{
    height: 100%;
    position:absolute;
}
.s-arrowA{
    width: .3%;
    position: relative;
    background: #000;
    height: 100%;
}
.s-arrowA:after {
    content: '';
    display: block;
    border: 7px transparent solid;
    border-bottom-color: #000;
    top: 96%;
    left: -6px;
    position: absolute;
}
.s-arrowSP{
    bottom: 100%;    
    width: .3%;
    position: relative;
    background: #fff;
    height: 100%;
}
.s-arrowSP:before {
    content: '';
    display: block;
    bottom: 101%;
    left: -6px;
    position: absolute;
    width: 0; 
    height: 0; 
    border-left: 7px solid transparent;
    border-right: 7px solid transparent;
    border-top: 7px solid #fff;
}
.s-setP{
    background-color: #e6e6e6;
    padding: 20px;
    height: 33%;
}
.s-titleSP{
    color: #000;
    font-size: 350%;
}
.s-gear{
    font-size: 3em;
    color: #000;
    text-align: left;
    cursor: pointer;
}
.s-circle{
    background-color: #47a3da;
    height: 42px;
    width: 42px;
    border-radius: 50%;
    color: #fff;
    position:fixed;
    right:2.3%;
    bottom:27.5%;
}
.s-circleValue{
    margin-top:6px;
    font-size: 2em;
}
.s-circleLabel{
    color: #afafb0;
    opacity: 0.8;
    position:fixed;
    right:2%;
    bottom:25.5%;
}
.s-setDiv{
    display: flex;
    justify-content: center;
    align-items: center;
}
.s-button{
    background-color: #dadada;
    border: none;
    height: 120px;
    width: 120px;
    border-radius: 50%;
    cursor: pointer;
}
.s-tempSP{
    font-size: 1000%;
    color:#000;
}
.s-minus{
    color: #3488b2; 
    font-size: 4em;
    margin-top:4px;
    margin-left: 1px;
}
.s-plus{
    color: #d40000; 
    font-size: 4em; 
    margin-top:4px; 
    margin-left: 1px;
}
.Cooling{
    background: #5e98e0;
    background: -webkit-linear-gradient(left bottom, #8fcffa, #5e98e0, #5d42b0);
    background:    -moz-linear-gradient(top right, #8fcffa, #5e98e0, #5d42b0);
    background:         linear-gradient(to top right, #8fcffa, #5e98e0, #5d42b0);
    width: 100%;
}
.Heating{
    background: #5e98e0;
    background: -webkit-linear-gradient(left bottom, #e5b356, #cf7b4c, #a83128);
    background:    -moz-linear-gradient(top right, #e5b356, #cf7b4c, #a83128);
    background:         linear-gradient(to top right, #e5b356, #cf7b4c, #a83128);
    width: 100%;
}
.Off{
    background-color: #e0e0e0;
    width: 100%;
}
.Heating1{
   background-color: #b8443f;
}
.Cooling1{
   background-color: #5d8eca;
}
.Off1{
   background-color: #47a3da;
}

This is the css to style the component

INIT

var self = this.ractive;
this.ractive.fire("obtainData");
var count = 0;

this.ractive.on("increment", function(event){
    var point   = this.get('mySetPoint');
    var val     = parseFloat(point.curVal);
    val += 1;
    if (val >= 84)
    val=84;
    if (val <= 0)
    val=0;
    finstack.eval('readById(' +point.id+ ').pointOverride(' +val+ ')').then(function(data) {
       if (data && data.result) {
           if (data.result.isErrorGrid) {
                console.error("Error occurred while updating value");
                return;
           }
          self.set('mySetPoint.curVal', val);
       } 
    }, function(err) {
        console.error("Error occurred while updating...", err);
    });
});

this.ractive.on("decrement", function(event) {
    var point   = this.get('mySetPoint');
    var val     = parseFloat(point.curVal);
    val -= 1;
    if (val >= 84)
    val=84;
    if (val <= 0)
    val=0;
    finstack.eval('readById(' +point.id+ ').pointOverride(' +val+ ')').then(function(data) {
       if (data && data.result) {
           if (data.result.isErrorGrid) {
                console.error("Error occurred while updating value");
                return;
           }
           self.set('mySetPoint.curVal', val);
       } 
    }, function(err) {
        console.error("Error occurred while updating...", err);
    });
});

this.ractive.on("actions", function(event) {
    var item    = this.get(event.keypath);
      var mainWin = window.parent;
     try {
        mainWin.app.ShowActionsFor (item.id);
        } catch (err) {console.error(err)}
});


Program

var roomT = this;
var roomSP = this;
var color = this;
var occColor = this;
var target = query("targetPoint");
finstack.eval('readAll((navName=="Room Temp") and floorRef=='+target.floorRef+' and siteRef=='+target.siteRef+' and equipRef=='+target.pointId+')', function(data){
roomT.myTemp=data.result.toObj()[0];
});

finstack.eval('readAll((navName=="Room Setpoint") and floorRef=='+target.floorRef+' and siteRef=='+target.siteRef+' and equipRef=='+target.pointId+')', function(data){
roomSP.mySetPoint=data.result.toObj()[0];
});

finstack.eval('readAll((navName=="OccHeat") and floorRef=='+target.floorRef+' and siteRef=='+target.siteRef+' and equipRef=='+target.pointId+')', function(data){
color.backColor=data.result.toObj()[0];
});

finstack.eval('readAll((navName=="Occ Mode") and floorRef=='+target.floorRef+' and siteRef=='+target.siteRef+' and equipRef=='+target.pointId+')', function(data){
occColor.checkMark=data.result.toObj()[0];
});

This is your program.  It sets the query for the model.  If your point's navName are not called Room Temp, Room Setpoint, OccHeat, and Occ Mode, they will have to be change accordingly on lines 6, 10, 14, and 18 (navName=="    ")



How To Make your Own

  • Size your graphics builder to 750X1046 and turn on scale to fit
     
  • Bring Ractive out from components on the left side menu

  • Under properties change the position so x and y are both 0, and set the width and height both to 100%
  • Then open the ractive editor, and copy and paste from the Ractive Code given above for template, model, style, and init


  • Next add a tag on your Ractive component and add a new program

    • Copy and paste the above code for program into the main part
    • Name your program, and set the program target filter to stackRactive
    • Top right of program editor, click the three dots, and select variables
    • Click the gear that appears as you hover over this
    • turn Invokes the Function on and change the dropdown to Custom Event
    • Type in obtainData in the line below and click the gray save
    • Click the blue save for your program

      Adding a Timer

    • Drag out a timer from components and add the tag timer
    • Edit your program by clicking the gear
    • Click the three dots in the top right corner and add a new variable
    • You can call it whatever you want in our case we named it timer
    • Next set it to the timer, and have it invoke the function, and set it to the custom event timer
    • Click the gray save, then the blue save and your done!
  • Save and you're done!