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