Skip to content

Commit

Permalink
Added virtual valves to the configruation and calculated valve states.
Browse files Browse the repository at this point in the history
  • Loading branch information
rstrouse authored and tagyoureit committed Sep 17, 2020
1 parent 4de62fd commit be71b79
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 11 deletions.
6 changes: 5 additions & 1 deletion controller/Equipment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,11 @@ export class Valve extends EqItem {
public get isIntake(): boolean { return utils.makeBool(this.data.isIntake); }
public set isIntake(val: boolean) { this.setDataVal('isIntake', val); }
public get isReturn(): boolean { return utils.makeBool(this.data.isReturn); }
public set isReturn(val: boolean) { this.setDataVal('isReturn', val); }
public set isReturn(val: boolean) { this.setDataVal('isVirtual', val); }
public get isVirtual(): boolean { return utils.makeBool(this.data.isReturn); }
public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
public get pinId(): number { return this.data.pinId || 0; }
public set pinId(val: number) { this.setDataVal('pinId', val); }
public get isActive(): boolean { return this.data.isActive; }
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
}
Expand Down
24 changes: 22 additions & 2 deletions controller/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import * as extend from 'extend';
import * as util from 'util';
import { setTimeout } from 'timers';
import { logger } from '../logger/Logger';
import { Timestamp, ControllerType } from './Constants';
import { Timestamp, ControllerType, utils } from './Constants';
import { webApp } from '../web/Server';
import { sys, ChemController } from './Equipment';
import { InvalidEquipmentIdError } from './Errors';
Expand Down Expand Up @@ -1063,13 +1063,33 @@ export class ValveState extends EqState {
public set id(val: number) { this.data.id = val; }
public get name(): string { return this.data.name; }
public set name(val: string) { this.setDataVal('name', val); }
public get type(): number { return typeof this.data.type !== 'undefined' ? this.data.type : -1; }
public get type(): number { return typeof this.data.type !== 'undefined' ? this.data.type.val : -1; }
public set type(val: number) {
if (this.type !== val) {
this.data.type = sys.board.valueMaps.valveTypes.transform(val);
this.hasChanged = true;
}
}
public get isDiverted(): boolean { return utils.makeBool(this.data.isDiverted); }
public set isDiverted(val: boolean) { this.setDataVal('isDiverted', val); }
public getExtended(): any {
let valve = sys.valves.getItemById(this.id);
let vstate = this.get(true);
if(valve.circuit !== 256) vstate.circuit = state.circuits.getInterfaceById(valve.circuit).get(true);
vstate.isIntake = utils.makeBool(valve.isIntake);
vstate.isReturn = utils.makeBool(valve.isReturn);
vstate.isVirtual = utils.makeBool(valve.isVirtual);
vstate.isActive = utils.makeBool(valve.isActive);
vstate.pinId = valve.pinId;
return vstate;
}
public emitEquipmentChange() {
if (typeof (webApp) !== 'undefined' && webApp) {
if (this.hasChanged) this.emitData(this.dataName, this.getExtended());
this.hasChanged = false;
state._dirtyList.removeEqState(this);
}
}
}
export class CoverStateCollection extends EqStateCollection<CoverState> {
public createItem(data: any): CoverState { return new CoverState(data); }
Expand Down
9 changes: 5 additions & 4 deletions controller/boards/IntelliCenterBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2846,15 +2846,16 @@ class IntelliCenterHeaterCommands extends HeaterCommands {

}
class IntelliCenterValveCommands extends ValveCommands {
public async setValveAsync(obj?: any) : Promise<Valve> {
public async setValveAsync(obj?: any): Promise<Valve> {
if (obj.isVirtual) return super.setValveAsync(obj);
let id = parseInt(obj.id, 10);
if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError('Valve Id has not been defined', obj.id, 'Valve'));
let valve = sys.valves.getItemById(id);
// [255, 0, 255][165, 63, 15, 16, 168, 20][9, 0, 9, 2, 86, 97, 108, 118, 101, 32, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0][4, 55]
// RKS: The valve messages are a bit unique since they are 0 based instead of 1s based. Our configuration includes
// the ability to set these valves appropriately via the interface by subtracting 1 from the circuit and the valve id. In
// shared body systems there is a gap for the additional intake/return valves that exist in i10d.
return new Promise<Valve>(function (resolve, reject) {
let id = parseInt(obj.id, 10);
if (isNaN(id)) reject(new InvalidEquipmentIdError('Valve Id has not been defined', obj.id, 'Valve'));
let valve = sys.valves.getItemById(id);
let v = extend(true, valve.get(true), obj);
let out = Outbound.create({
action: 168,
Expand Down
43 changes: 41 additions & 2 deletions controller/boards/SystemBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,7 @@ export class CircuitCommands extends BoardCommands {
}
}
}
sys.board.valves.syncValveStates();
state.emitEquipmentChanges();
sys.board.virtualPumpControllers.start();
return Promise.resolve(state.circuits.getInterfaceById(circ.id));
Expand Down Expand Up @@ -1915,6 +1916,7 @@ export class FeatureCommands extends BoardCommands {
let feature = sys.features.getItemById(id);
let fstate = state.features.getItemById(feature.id, feature.isActive !== false);
fstate.isOn = val;
sys.board.valves.syncValveStates();
sys.board.virtualPumpControllers.start();
state.emitEquipmentChanges();
return Promise.resolve(fstate.get(true));
Expand All @@ -1937,6 +1939,7 @@ export class FeatureCommands extends BoardCommands {
}
let sgrp = state.circuitGroups.getItemById(grp.id);
sgrp.isOn = bIsOn && grp.isActive;
sys.board.valves.syncValveStates();
state.emitEquipmentChanges();
}
}
Expand Down Expand Up @@ -2378,14 +2381,50 @@ export class HeaterCommands extends BoardCommands {
}
export class ValveCommands extends BoardCommands {
public async setValveAsync(obj: any): Promise<Valve> {
let id = parseInt(obj.id, 10);
// The following code will make sure we do not encroach on any valves defined by the OCP.
obj.isVirtual = true;
if (isNaN(id) || id <= 0) id = Math.max(sys.valves.getMaxId(false), 49) + 1;
return new Promise<Valve>(function (resolve, reject) {
let id = parseInt(obj.id, 10);
if (isNaN(id)) reject(new InvalidEquipmentIdError('Valve Id has not been defined', obj.id, 'Valve'));
let valve = sys.valves.getItemById(id, false);
if (id < 50) reject(new InvalidEquipmentDataError('Virtual valves must be defined with an id >= 50.', obj.id, 'Valve'));
let valve = sys.valves.getItemById(id, true);
obj.id = id;
for (var s in obj) valve[s] = obj[s];
sys.board.valves.syncValveStates();
resolve(valve);
});
}
public async deleteValveAsync(obj: any): Promise<Valve> {
let id = parseInt(obj.id, 10);
// The following code will make sure we do not encroach on any valves defined by the OCP.
return new Promise<Valve>(function (resolve, reject) {
if (isNaN(id)) reject(new InvalidEquipmentIdError('Valve Id has not been defined', obj.id, 'Valve'));
let valve = sys.valves.getItemById(id, false);
let vstate = state.valves.getItemById(id);
valve.isActive = false;
vstate.hasChanged = true;
vstate.emitEquipmentChange();
sys.valves.removeItemById(id);
state.valves.removeItemById(id);

resolve(valve);
});
}

public syncValveStates() {
for (let i = 0; i < sys.valves.length; i++) {
// Run through all the valves to see whether they should be triggered or not.
let valve = sys.valves.getItemByIndex(i);
if (valve.circuit > 0) {
let circ = state.circuits.getInterfaceById(valve.circuit);
let vstate = state.valves.getItemById(valve.id, true);
vstate.type = valve.type;
vstate.name = valve.name;
vstate.isDiverted = utils.makeBool(circ.isOn);
}
}
}
}
export class ChemControllerCommands extends BoardCommands {
public async setChemControllerAsync(data: any): Promise<ChemController> {
Expand Down
1 change: 1 addition & 0 deletions controller/comms/messages/config/ExternalMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class ExternalMessage {
let valve = sys.valves.getItemById(msg.extractPayloadByte(2) + 1);
valve.circuit = msg.extractPayloadByte(3) + 1;
valve.name = msg.extractPayloadString(4, 16);
valve.isVirtual = false;
}
public static processPool(msg: Inbound) {
switch (msg.extractPayloadByte(2)) {
Expand Down
1 change: 1 addition & 0 deletions controller/comms/messages/config/ValveMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export class ValveMessage {
// what is payload[0]?
for (let ndx = 4, id = 1; id <= sys.equipment.maxValves;) {
let valve = sys.valves.getItemById(id, true);
valve.isVirtual = false;
valve.type = 0;
if (id === 3){
valve.circuit = 6; // pool/spa -- fix
Expand Down
2 changes: 2 additions & 0 deletions controller/comms/messages/status/EquipmentStateMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ export class EquipmentStateMessage {
// to be available on message 30-15 and 168-15.
//EquipmentStateMessage.processFeatureState(msg);
sys.board.circuits.syncVirtualCircuitStates();
sys.board.valves.syncValveStates();
state.emitControllerChange();
state.emitEquipmentChanges();
break;
Expand All @@ -537,6 +538,7 @@ export class EquipmentStateMessage {
// This will toggle the group states depending on the state of the individual circuits.
sys.board.features.syncGroupStates();
sys.board.circuits.syncVirtualCircuitStates();
sys.board.valves.syncValveStates();
state.emitControllerChange();
state.emitEquipmentChanges();
break;
Expand Down
12 changes: 12 additions & 0 deletions defaultConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@
"host": "",
"port": 3480
}
},
"valveRelay": {
"name": "Valve Relays",
"enabled": false,
"fileName": "valveRelays.json",
"vars": {
"valveIds": []
},
"options": {
"host": "0.0.0.0",
"port": 8081
}
}
}
},
Expand Down
20 changes: 20 additions & 0 deletions web/bindings/valveRelays.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"context": {
"name": "valveRelay",
"options": {
"method": "GET",
"path": "/@bind=data.pinId;/@bind=data.isDiverted ? 'on' : 'off';",
"headers": {
"CONTENT-TYPE": "application/json"
}
},
"vars": {}
},
"events": [
{
"name": "valve",
"filter": "@bind=data.isVirtual;",
"description": "Send commands to turn on or off the valve relay based upon the valve emit."
}
]
}
1 change: 1 addition & 0 deletions web/interfaces/baseInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class BaseInterfaceBindings {
export class InterfaceEvent {
public name: string;
public enabled: boolean = true;
public filter: string;
public options: any = {};
public body: any = {};
public vars: any = {};
Expand Down
9 changes: 7 additions & 2 deletions web/interfaces/httpInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export class HttpInterfaceBindings extends BaseInterfaceBindings {
let e = evts[i];
if (typeof e.enabled !== 'undefined' && !e.enabled) continue;
let opts = extend(true, baseOpts, e.options);
// Figure out whether we need to check the filter.
if (typeof e.filter !== 'undefined') {
this.buildTokens(e.filter, evt, toks, e, data[0]);
if (eval(this.replaceTokens(e.filter, toks)) === false) continue;
}

// If we are still waiting on mdns then blow this off.
if ((typeof opts.hostname === 'undefined' || !opts.hostname) && (typeof opts.host === 'undefined' || !opts.host || opts.host === '*')) {
Expand All @@ -63,7 +68,7 @@ export class HttpInterfaceBindings extends BaseInterfaceBindings {
//case 'application/json':
//case 'json':
default:
sbody = JSON.stringify(e.body);
sbody = typeof e.body !== 'undefined' ? JSON.stringify(e.body) : '';
break;
// We may need an XML output and can add transforms for that
// later. There isn't a native xslt processor in node and most
Expand All @@ -87,7 +92,7 @@ export class HttpInterfaceBindings extends BaseInterfaceBindings {
}
if (typeof opts.path !== 'undefined') opts.path = encodeURI(opts.path); // Encode the data just in case we have spaces.
// opts.headers["CONTENT-LENGTH"] = Buffer.byteLength(sbody || '');
logger.verbose(`Sending [${ evt }] request to ${ this.cfg.name }: ${ JSON.stringify(opts) }`);
logger.verbose(`Sending [${evt}] request to ${this.cfg.name}: ${JSON.stringify(opts)}`);
let req: http.ClientRequest;
// We should now have all the tokens. Put together the request.
if (typeof sbody !== 'undefined') {
Expand Down
9 changes: 9 additions & 0 deletions web/services/config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,15 @@ export class ConfigRoute {
}
catch (err) { next(err); }
});
app.delete('/config/valve', async (req, res, next) => {
// Update a valve.
try {
let valve = await sys.board.valves.deleteValveAsync(req.body);
return res.status(200).send((valve).get(true));
}
catch (err) { next(err); }
});

app.put('/config/body', async (req, res, next) => {
// Change the body attributes.
try {
Expand Down

0 comments on commit be71b79

Please sign in to comment.