node-red-contrib-smartnora
Version:
Google Smart Home integration via Smart Nora https://smart-nora.eu/
329 lines (310 loc) • 13.2 kB
HTML
<script type="text/javascript">
const SENSOR_TRAIT_AVAILABLE_SENSORS = [{
name: 'AirQuality',
states: [
'healthy', 'moderate', 'unhealthy', 'unhealthy for sensitive groups',
'very unhealthy', 'hazardous', 'good', 'fair', 'poor', 'very poor',
'severe'
],
numeric: 'AQI'
}, {
name: 'CarbonMonoxideLevel',
states: ['carbon monoxide detected', 'high', 'no carbon monoxide detected'],
numeric: 'PARTS_PER_MILLION',
}, {
name: 'SmokeLevel',
states: ['smoke detected', 'high', 'no smoke detected'],
numeric: 'PARTS_PER_MILLION',
}, {
name: 'FilterCleanliness',
states: ['clean', 'dirty', 'needs replacement'],
numeric: null,
}, {
name: 'WaterLeak',
states: ['leak', 'no leak'],
numeric: null,
}, {
name: 'RainDetection',
states: ['rain detected', 'no rain detected'],
numeric: null,
}, {
name: 'FilterLifeTime',
states: ['new', 'good', 'replace soon', 'replace now'],
numeric: 'PERCENTAGE',
}, {
name: ['PreFilterLifeTime', 'HEPAFilterLifeTime', 'Max2FilterLifeTime'],
states: null,
numeric: 'PERCENTAGE',
}, {
name: 'CarbonDioxideLevel',
states: null,
numeric: 'PARTS_PER_MILLION',
}, {
name: ['PM2.5', 'PM10'],
states: null,
numeric: 'MICROGRAMS_PER_CUBIC_METER',
}, {
name: 'VolatileOrganicCompounds',
states: null,
numeric: 'PARTS_PER_MILLION',
}];
RED.nodes.registerType('noraf-sensor', {
category: 'nora',
color: 'rgb(235, 227, 141)',
icon: 'assistant.png',
defaults: {
devicename: {
value: 'Sensor',
required: true,
},
roomhint: {
value: ''
},
name: {
value: ''
},
temperature: {
value: false,
},
unit: {
value: 'C',
validate: function (v) {
return v === 'C' || v === 'F';
},
},
humidity: {
value: false,
},
passthru: {
value: false,
},
nora: {
type: 'noraf-config',
required: true
},
topic: {
value: '',
},
filter: {
value: false,
},
sensorSupport: {
value: false,
},
sensorType: {
value: 'AirQuality',
},
sensorStates: {
value: [],
},
sensorStatesThatNotify: {
value: [],
},
sensorNumeric: {
value: true,
},
openCloseSupport: {
value: false,
},
openCloseDiscrete: {
value: false,
},
onOffSupport: {
value: false,
},
},
inputs: 1,
outputs: 0,
paletteLabel: 'sensor',
label: function () {
return this.name || this.devicename || 'sensor';
},
oneditprepare: function () {
if ((this.unit || 'C') === 'C') {
$('#unit-celsius').prop('checked', true);
} else {
$('#unit-fahrenheit').prop('checked', true);
}
$('#node-input-temperature').change(function () {
$('.temperature-support')[$(this).is(':checked') ? 'show' : 'hide']();
});
$('#node-input-sensorSupport').change(function () {
$('.sensor-support')[$(this).is(':checked') ? 'show' : 'hide']();
});
$('#node-input-openCloseSupport').change(function () {
$('.openclose-support')[$(this).is(':checked') ? 'show' : 'hide']();
});
const selectedStates = this.sensorStates || [];
const selectedSensorStatesThatNotify = this.sensorStatesThatNotify || [];
$('#node-input-sensorType').change(function () {
const selectedType = $(this).val();
const found = SENSOR_TRAIT_AVAILABLE_SENSORS.find(s =>
s.name === selectedType || s.name.includes(selectedType)) || {};
$('#states-container > *').remove();
$('.sensor-states')[found.states ? 'show' : 'hide']();
$('.sensor-numeric')[found.states && found.numeric ? 'show' : 'hide']();
if (!Array.isArray(found.states)) return;
const stateContainer = $('#states-container');
stateContainer.html('<strong>On</strong><strong>Value</strong><strong>Notification</strong>');
for (const [index, state] of found.states.entries()) {
$('<input/>', {
id: `state-${index}`,
type: 'checkbox',
'data-state': state,
class: 'sensor-state',
checked: selectedStates.includes(state),
state,
})
.text(state)
.appendTo(stateContainer);
$('<label/>', {
for: `state-${index}`,
}).html(`<code>${state}</code>`).appendTo(stateContainer);
$('<input/>', {
id: `state-${index}`,
type: 'checkbox',
'data-notification-state': state,
class: 'sensor-notification-state',
checked: selectedSensorStatesThatNotify.includes(state),
state,
})
.text(state)
.appendTo(stateContainer);
}
});
},
oneditsave: function () {
this.unit = $('#unit-celsius').prop('checked') ? 'C' : 'F';
const sensorStates = [];
const sensorStatesThatNotify = [];
$('.sensor-state:checked').each(function () {
sensorStates.push($(this).attr('data-state'));
});
$('.sensor-notification-state:checked').each(function () {
sensorStatesThatNotify.push($(this).attr('data-notification-state'));
});
this.sensorStates = sensorStates;
this.sensorStatesThatNotify = sensorStatesThatNotify;
},
});
</script>
<script type="text/x-red" data-template-name="noraf-sensor">
<style>
#states-container {
display: grid;
grid-template-columns: auto 1fr auto;
gap: 5px 15px;
align-items: center;
}
#states-container input[type=checkbox] {
width: unset;
margin: 0;
}
#states-container input[data-notification-state] {
justify-self: end;
}
#states-container label {
width: unset;
margin: 0;
}
.trait-support {
margin-left: 5px;
border-left: 1px solid lightgray;
padding-left: 10px;
padding-top: 10px;
margin-top: -15px;
margin-bottom: 5px;
}
</style>
<div class="form-row">
<label for="node-input-nora"><i class="fa fa-table"></i> Config</label>
<input type="text" id="node-input-nora">
</div>
<div class="form-row">
<label for="node-input-devicename"><i class="fa fa-i-cursor"></i> Sensor</label>
<input type="text" id="node-input-devicename">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-filter"><i class="fa fa-filter"></i> Ignore input messages that don't match the <code>topic</code> value: </label>
<input type="checkbox" id="node-input-filter" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-temperature"><i class="fa fa-thermometer-half"></i> Temperature </label>
<input type="checkbox" id="node-input-temperature" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="temperature-support trait-support">
<div class="form-row">
<label style="width:auto"><i class="fa fa-thermometer-half"></i> Temperature Unit: </label>
<input style="width:auto;margin:0" id="unit-celsius" type="radio" name="unit" value="C"><label style="width:40px" for="unit-celsius"> C</label>
<input style="width:auto;margin:0" id="unit-fahrenheit" type="radio" name="unit" value="F"><label style="width:40px" for="unit-fahrenheit"> F</label>
</div>
</div>
<div class="form-row">
<label style="width:auto" for="node-input-humidity"><i class="fa fa-percent"></i> Humidity </label>
<input type="checkbox" id="node-input-humidity" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-onOffSupport"><i class="fa fa-power-off"></i> On/Off </label>
<input type="checkbox" id="node-input-onOffSupport" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-openCloseSupport"><i class="fa fa-folder-open-o"></i> Open/close </label>
<input type="checkbox" id="node-input-openCloseSupport" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="openclose-support trait-support">
<div class="form-row">
<label style="width:auto" for="node-input-openCloseDiscrete"><i class="fa fa-arrow-right"></i> Discrete only open/close: </label>
<input type="checkbox" id="node-input-openCloseDiscrete" style="display:inline-block; width:auto; vertical-align:top;">
</div>
</div>
<div class="form-row">
<label style="width:auto" for="node-input-sensorSupport"><i class="fa fa-barcode"></i> Sensor </label>
<input type="checkbox" id="node-input-sensorSupport" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="sensor-support trait-support">
<div class="form-row">
<label for="node-input-sensorType"><i class="fa fa-i-cursor"></i> Type</label>
<select id="node-input-sensorType">
<option value="AirQuality">Air Quality</option>
<option value="CarbonDioxideLevel">Carbon Dioxide Level</option>
<option value="CarbonMonoxideLevel">Carbon Monoxide Level</option>
<option value="FilterCleanliness">Filter Cleanliness</option>
<option value="FilterLifeTime">Filter Life Time</option>
<option value="PreFilterLifeTime">Filter Life Time (Pre)</option>
<option value="HEPAFilterLifeTime">Filter Life Time (HEPA)</option>
<option value="Max2FilterLifeTime">Filter Life Time (Max 2)</option>
<option value="PM2.5">PM2.5</option>
<option value="PM10">PM10</option>
<option value="RainDetection">Rain Detection</option>
<option value="SmokeLevel">Smoke Level</option>
<option value="VolatileOrganicCompounds">Volatile Organic Compounds</option>
<option value="WaterLeak">Water Leak</option>
</select>
</div>
<div class="form-row sensor-numeric">
<label style="width:auto" for="node-input-sensorNumeric"><i class="fa fa-sort-numeric-desc"></i> Numeric capability </label>
<input type="checkbox" id="node-input-sensorNumeric" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row sensor-states">
<label style="width:100%"><i class="fa fa-cog"></i> Supported states</label>
<div id="states-container"></div>
</div>
</div>
<div class="form-row">
<label for="node-input-roomhint"><i class="fa fa-i-cursor"></i> Room Hint</label>
<input type="text" id="node-input-roomhint">
</div>
<div class="form-row">
<label for="node-input-topic" style="padding-left:25px; margin-right:-25px">Topic</label>
<input type="text" id="node-input-topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
</div>
</script>
<script type="text/x-red" data-help-name="noraf-sensor">
<p>
<a href="https://github.com/andrei-tatar/node-red-contrib-smartnora/blob/master/doc/nodes/sensor/README.md">https://github.com/andrei-tatar/node-red-contrib-smartnora/blob/master/doc/nodes/sensor/README.md</a>
</p>
</script>