11 Commits

Author SHA1 Message Date
861fec9565 Update changelog, add markdown linting 2025-01-05 12:25:08 -06:00
69c2c67336 Increase package version 2025-01-05 11:59:47 -06:00
50d71a2858 Add GetCircuitStatus message 2025-01-05 11:59:03 -06:00
abe9ba7d95 Rename Item to Object
The API all calls these objects so best to go with the flow.

This is a breaking change.
2025-01-05 11:56:53 -06:00
21d62549d3 Increase package version 2025-01-04 15:51:26 -06:00
9455ae224c Update vscode config 2025-01-04 15:51:19 -06:00
e8ebfe8e02 Add connected event
This enables event-based consumers to more easily react to connection completion.
2025-01-04 15:51:01 -06:00
3031bc2079 Update exports, rename example
This gives better control over importing in non-module environments and removes the example code as the default import which was running when loading up the library.
2025-01-04 15:50:24 -06:00
9e332038c6 Add ability to specify multicast interface
When you have multiple network adapters/interfaces, the system can choose the wrong one. This allows you to be explicit about which one you broadcast on.
2025-01-04 15:45:22 -06:00
033627d698 Add more docs, error handling 2025-01-04 13:15:08 -06:00
f59cc3438c Add connection events 2025-01-04 13:09:07 -06:00
39 changed files with 1368 additions and 210 deletions

3
.markdownlint-cli2.jsonc Normal file
View File

@ -0,0 +1,3 @@
{
"gitignore": true,
}

7
.markdownlint.json Normal file
View File

@ -0,0 +1,7 @@
{
"default": true,
"MD024": {
"siblings_only": true
},
"line-length": false
}

7
.vscode/launch.json vendored
View File

@ -9,9 +9,12 @@
"request": "launch",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/index.ts",
"program": "${workspaceFolder}/example.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/**/*.js"]
"outFiles": ["${workspaceFolder}/**/*.js"],
"env": {
"DEBUG": "ic:*"
}
}
]
}

View File

@ -2,5 +2,35 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v0.1.0] - 2025-01-05
### Added
- Added GetCircuitStatus message for getting current status of all connected circuits.
### Changed
- [Breaking change] Renamed Item to Object in, e.g., messages.SetObjectStatus()
## [v0.0.2] - 2025-01-04
### Added
- Added "close", "open", "error", "timeout" events to Unit.
- Added more documentation and error checking to Unit methods.
- Added ability to specify which interface to broadcast the unit search on. Specify your adapter's address to pick which one to use, e.g. "10.0.0.3", "172.16.0.25". Leaving this argument out will use the previous behavior, which allows the system to choose the adapter it wants to use.
- Added FindUnits and Unit as exports from the base package to make importing and using the library easier in non-module consumers.
- Added "connected" event to Unit which runs once a connection is established and requests may be sent.
### Fixed
- Fixed the default import being the example code which was finding and running commands against controllers.
## [v0.0.1] - 2025-01-04
### Added
- Initial release. Includes basic ability to find and connect to an IntelliCenter unit and query and make changes to it.

1
dist/example.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export {};

63
dist/example.js vendored Normal file
View File

@ -0,0 +1,63 @@
"use strict";
import { FindUnits, Unit } from "./index.js";
import { messages } from "./messages/messages.js";
console.log("searching...");
const f = new FindUnits();
const units = await f.searchAsync(1000);
f.close();
console.log("Discovered units:", units);
if (units.length === 0) {
throw new Error("no IntelliCenter units found, exiting.");
}
if (units.length > 1) {
throw new Error(`found more than one IntelliCenter unit, unsure which one to use. ${JSON.stringify(units)}`);
}
const endpoint = units[0].addressStr;
const port = units[0].port;
// const endpoint = "10.0.0.41";
// const port = 6680;
console.log("connecting to intellicenter device at", endpoint, "port", port);
const unit = new Unit(endpoint, port);
await unit.connect();
console.log("connected");
unit.on("notify", (msg) => {
console.log("received notify:", msg);
});
console.log("subscribing for updates...");
let resp = await unit.send(messages.SubscribeToUpdates("B1202", "LOTMP"));
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Info request...");
resp = await unit.send(messages.GetSystemInformation());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Config request...");
resp = await unit.send(messages.GetSystemConfiguration());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Body Status request...");
resp = await unit.send(messages.GetBodyStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Chemical Status request...");
resp = await unit.send(messages.GetChemicalStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Heaters request...");
resp = await unit.send(messages.GetHeaters());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Schedule request...");
resp = await unit.send(messages.GetSchedule());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Circuit Status request...");
resp = await unit.send(messages.GetCircuitStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Setpoint request...");
// resp = await unit.send(messages.SetSetpoint("B1202", 97));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off pool...");
// resp = await unit.send(messages.SetObjectStatus("B1101", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off water feature...");
// resp = await unit.send(messages.SetObjectStatus("C0003", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Heatmode request...");
// resp = await unit.send(messages.SetHeatMode("B1202", true));
// console.log("got response:", JSON.stringify(resp, null, 2));
unit.close();
//# sourceMappingURL=example.js.map

1
dist/example.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"example.js","sourceRoot":"","sources":["../example.ts"],"names":[],"mappings":"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"}

8
dist/finder.d.ts vendored
View File

@ -20,7 +20,13 @@ export declare class UnitInfo {
* * `"serverFound"` - fired immediately when an IntelliCenter unit has been located; receives a {@linkcode UnitInfo} argument
*/
export declare class FindUnits extends EventEmitter {
constructor();
broadcastInterface?: string | undefined;
/**
* Creates a new finder.
*
* @param broadcastInterface the address of the interface to send the broadcast to. If not specified, will use system selection. Only necessary if you have more than one network adapter/interface and want to search on a specific one.
*/
constructor(broadcastInterface?: string | undefined);
private finder;
private bound;
private message;

12
dist/finder.js vendored
View File

@ -31,8 +31,15 @@ export class UnitInfo {
* * `"serverFound"` - fired immediately when an IntelliCenter unit has been located; receives a {@linkcode UnitInfo} argument
*/
export class FindUnits extends EventEmitter {
constructor() {
broadcastInterface;
/**
* Creates a new finder.
*
* @param broadcastInterface the address of the interface to send the broadcast to. If not specified, will use system selection. Only necessary if you have more than one network adapter/interface and want to search on a specific one.
*/
constructor(broadcastInterface) {
super();
this.broadcastInterface = broadcastInterface;
// construct mDNS packet to ping for intellicenter controllers
this.message = Buffer.alloc(34);
let offset = 0;
@ -54,6 +61,9 @@ export class FindUnits extends EventEmitter {
this.finder = createSocket("udp4");
this.finder
.on("listening", () => {
if (this.broadcastInterface) {
this.finder.setMulticastInterface(this.broadcastInterface);
}
this.finder.setBroadcast(true);
this.finder.setMulticastTTL(128);
if (!this.bound) {

2
dist/finder.js.map vendored

File diff suppressed because one or more lines are too long

4
dist/index.d.ts vendored
View File

@ -1 +1,3 @@
export {};
import { FindUnits } from "./finder.js";
import { Unit } from "./unit.js";
export { FindUnits, Unit };

59
dist/index.js vendored
View File

@ -1,61 +1,4 @@
"use strict";
import { FindUnits } from "./finder.js";
import { messages } from "./messages/messages.js";
import { Unit } from "./unit.js";
console.log("searching...");
const f = new FindUnits();
const units = await f.searchAsync(1000);
f.close();
console.log("Discovered units:", units);
if (units.length === 0) {
throw new Error("no IntelliCenter units found, exiting.");
}
if (units.length > 1) {
throw new Error(`found more than one IntelliCenter unit, unsure which one to use. ${JSON.stringify(units)}`);
}
const endpoint = units[0].addressStr;
const port = units[0].port;
// const endpoint = "10.0.0.41";
// const port = 6680;
console.log("connecting to intellicenter device at", endpoint, "port", port);
const unit = new Unit(endpoint, port);
await unit.connect();
console.log("connected");
unit.on("notify", (msg) => {
console.log("received notify:", msg);
});
console.log("subscribing for updates...");
let resp = await unit.send(messages.SubscribeToUpdates("B1202", "LOTMP"));
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Info request...");
resp = await unit.send(messages.GetSystemInformation());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Config request...");
resp = await unit.send(messages.GetSystemConfiguration());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Body Status request...");
resp = await unit.send(messages.GetBodyStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Chemical Status request...");
resp = await unit.send(messages.GetChemicalStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Heaters request...");
resp = await unit.send(messages.GetHeaters());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Schedule request...");
resp = await unit.send(messages.GetSchedule());
console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Setpoint request...");
// resp = await unit.send(messages.SetSetpoint("B1202", 97));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off pool...");
// resp = await unit.send(messages.SetItemStatus("B1101", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off water feature...");
// resp = await unit.send(messages.SetItemStatus("C0003", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Heatmode request...");
// resp = await unit.send(messages.SetHeatMode("B1202", true));
// console.log("got response:", JSON.stringify(resp, null, 2));
unit.close();
export { FindUnits, Unit };
//# sourceMappingURL=index.js.map

2
dist/index.js.map vendored
View File

@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC"}

9
dist/messages/circuit-status.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
import { ICRequest } from "./request.js";
/**
* Requests the list of circuits known to this controller.
*
* The response contains an `objectList` populated with circuit information.
*
* @returns the object used to issue this request
*/
export declare function GetCircuitStatus(): ICRequest;

37
dist/messages/circuit-status.js vendored Normal file
View File

@ -0,0 +1,37 @@
import { GetRequest, ICRequestObj } from "./request.js";
/**
* Requests the list of circuits known to this controller.
*
* The response contains an `objectList` populated with circuit information.
*
* @returns the object used to issue this request
*/
export function GetCircuitStatus() {
const req = GetRequest();
req.command = "GetParamList";
req.condition = "OBJTYP = CIRCUIT";
req.objectList = [];
const reqObj = new ICRequestObj();
reqObj.objnam = "ALL";
reqObj.keys = [
"OBJNAM",
"OBJTYP",
"SUBTYP",
"STATUS",
"BODY",
"SNAME",
"HNAME",
"FREEZE",
"DNTSTP",
"HNAME",
"TIME",
"FEATR",
"USAGE",
"LIMIT",
"USE",
"SHOMNU",
];
req.objectList.push(reqObj);
return req;
}
//# sourceMappingURL=circuit-status.js.map

1
dist/messages/circuit-status.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"circuit-status.js","sourceRoot":"","sources":["../../messages/circuit-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,YAAY,EAAE,MAAM,cAAc,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,GAAG,CAAC,OAAO,GAAG,cAAc,CAAC;IAC7B,GAAG,CAAC,SAAS,GAAG,kBAAkB,CAAC;IACnC,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC;IAEpB,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;IAClC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,MAAM,CAAC,IAAI,GAAG;QACZ,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,MAAM;QACN,OAAO;QACP,OAAO;QACP,QAAQ;QACR,QAAQ;QACR,OAAO;QACP,MAAM;QACN,OAAO;QACP,OAAO;QACP,OAAO;QACP,KAAK;QACL,QAAQ;KACT,CAAC;IACF,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE5B,OAAO,GAAG,CAAC;AACb,CAAC"}

View File

@ -3,12 +3,12 @@ import { ICRequest } from "./request.js";
* Requests the configuration of bodies and circuits available to this controller.
*
* The response contains the list of bodies and circuits under the `answer` field.
* Each item has an `objnam` that should be used to reference that item for future requests,
* and `params`.`SNAME` is the user-entered friendly name that can be displayed for the item.
* `params`.`OBJTYP` will be either BODY or CIRCUIT depending on the item it's describing.
* Each object has an `objnam` that should be used to reference that object for future requests,
* and `params`.`SNAME` is the user-entered friendly name that can be displayed for the object.
* `params`.`OBJTYP` will be either BODY or CIRCUIT depending on the object it's describing.
*
* Some items, such as the Pool body, will have the `params`.`OBJLIST` array populated with
* a series of attached items such as a chlorinator device.
* Some objects, such as the Pool body, will have the `params`.`OBJLIST` array populated with
* a series of attached objects such as a chlorinator device.
*
* @returns the object used to issue this request
*/

View File

@ -3,12 +3,12 @@ import { GetRequest } from "./request.js";
* Requests the configuration of bodies and circuits available to this controller.
*
* The response contains the list of bodies and circuits under the `answer` field.
* Each item has an `objnam` that should be used to reference that item for future requests,
* and `params`.`SNAME` is the user-entered friendly name that can be displayed for the item.
* `params`.`OBJTYP` will be either BODY or CIRCUIT depending on the item it's describing.
* Each object has an `objnam` that should be used to reference that object for future requests,
* and `params`.`SNAME` is the user-entered friendly name that can be displayed for the object.
* `params`.`OBJTYP` will be either BODY or CIRCUIT depending on the object it's describing.
*
* Some items, such as the Pool body, will have the `params`.`OBJLIST` array populated with
* a series of attached items such as a chlorinator device.
* Some objects, such as the Pool body, will have the `params`.`OBJLIST` array populated with
* a series of attached objects such as a chlorinator device.
*
* @returns the object used to issue this request
*/

View File

@ -1,22 +1,24 @@
import { GetBodyStatus } from "./body-status.js";
import { GetChemicalStatus } from "./chem-status.js";
import { GetCircuitStatus } from "./circuit-status.js";
import { GetSystemConfiguration } from "./configuration.js";
import { GetHeaters } from "./get-heater.js";
import { SubscribeToUpdates } from "./notify.js";
import { GetSchedule } from "./schedule.js";
import { SetHeatMode } from "./set-heater.js";
import { SetItemStatus } from "./set-status.js";
import { SetObjectStatus } from "./set-object-status.js";
import { SetSetpoint } from "./setpoint.js";
import { GetSystemInformation } from "./system-info.js";
export declare const messages: {
GetBodyStatus: typeof GetBodyStatus;
GetChemicalStatus: typeof GetChemicalStatus;
GetCircuitStatus: typeof GetCircuitStatus;
GetHeaters: typeof GetHeaters;
GetSchedule: typeof GetSchedule;
GetSystemConfiguration: typeof GetSystemConfiguration;
GetSystemInformation: typeof GetSystemInformation;
SetHeatMode: typeof SetHeatMode;
SetItemStatus: typeof SetItemStatus;
SetObjectStatus: typeof SetObjectStatus;
SetSetpoint: typeof SetSetpoint;
SubscribeToUpdates: typeof SubscribeToUpdates;
};

View File

@ -1,22 +1,24 @@
import { GetBodyStatus } from "./body-status.js";
import { GetChemicalStatus } from "./chem-status.js";
import { GetCircuitStatus } from "./circuit-status.js";
import { GetSystemConfiguration } from "./configuration.js";
import { GetHeaters } from "./get-heater.js";
import { SubscribeToUpdates } from "./notify.js";
import { GetSchedule } from "./schedule.js";
import { SetHeatMode } from "./set-heater.js";
import { SetItemStatus } from "./set-status.js";
import { SetObjectStatus } from "./set-object-status.js";
import { SetSetpoint } from "./setpoint.js";
import { GetSystemInformation } from "./system-info.js";
export const messages = {
GetBodyStatus,
GetChemicalStatus,
GetCircuitStatus,
GetHeaters,
GetSchedule,
GetSystemConfiguration,
GetSystemInformation,
SetHeatMode,
SetItemStatus,
SetObjectStatus,
SetSetpoint,
SubscribeToUpdates,
};

View File

@ -1 +1 @@
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../messages/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,aAAa;IACb,iBAAiB;IACjB,UAAU;IACV,WAAW;IACX,sBAAsB;IACtB,oBAAoB;IACpB,WAAW;IACX,aAAa;IACb,WAAW;IACX,kBAAkB;CACnB,CAAC"}
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../messages/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,aAAa;IACb,iBAAiB;IACjB,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,sBAAsB;IACtB,oBAAoB;IACpB,WAAW;IACX,eAAe;IACf,WAAW;IACX,kBAAkB;CACnB,CAAC"}

9
dist/messages/set-object-status.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
import { ICRequest } from "./request.js";
/**
* Requests to change the status of objects known to this controller.
*
* Turns one or more objects on or off. Use the `objnam` of the circuit to be set.
*
* @returns the object used to issue this request
*/
export declare function SetObjectStatus(object: string | string[], status: boolean): ICRequest;

View File

@ -1,24 +1,24 @@
import { ICParam } from "./param.js";
import { GetRequest, ICRequestObj } from "./request.js";
/**
* Requests to change the status of items known to this controller.
* Requests to change the status of objects known to this controller.
*
* Turns one or more items on or off. Use the `objnam` of the circuit to be set.
* Turns one or more objects on or off. Use the `objnam` of the circuit to be set.
*
* @returns the object used to issue this request
*/
export function SetItemStatus(item, status) {
export function SetObjectStatus(object, status) {
const req = GetRequest();
req.command = "SetParamList";
req.objectList = [];
let items;
if (Array.isArray(item)) {
items = item;
let objects;
if (Array.isArray(object)) {
objects = object;
}
else {
items = [item];
objects = [object];
}
for (const i of items) {
for (const i of objects) {
const reqObj = new ICRequestObj();
reqObj.objnam = i;
reqObj.params = new ICParam();
@ -27,4 +27,4 @@ export function SetItemStatus(item, status) {
}
return req;
}
//# sourceMappingURL=set-status.js.map
//# sourceMappingURL=set-object-status.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"set-object-status.js","sourceRoot":"","sources":["../../messages/set-object-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,UAAU,EAAa,YAAY,EAAE,MAAM,cAAc,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAyB,EACzB,MAAe;IAEf,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,GAAG,CAAC,OAAO,GAAG,cAAc,CAAC;IAC7B,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC;IAEpB,IAAI,OAAiB,CAAC;IACtB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAClC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAClB,MAAM,CAAC,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7C,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}

View File

@ -1,9 +0,0 @@
import { ICRequest } from "./request.js";
/**
* Requests to change the status of items known to this controller.
*
* Turns one or more items on or off. Use the `objnam` of the circuit to be set.
*
* @returns the object used to issue this request
*/
export declare function SetItemStatus(item: string | string[], status: boolean): ICRequest;

View File

@ -1 +0,0 @@
{"version":3,"file":"set-status.js","sourceRoot":"","sources":["../../messages/set-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,UAAU,EAAa,YAAY,EAAE,MAAM,cAAc,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAuB,EACvB,MAAe;IAEf,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,GAAG,CAAC,OAAO,GAAG,cAAc,CAAC;IAC7B,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC;IAEpB,IAAI,KAAe,CAAC;IACpB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAClC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAClB,MAAM,CAAC,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7C,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}

7
dist/unit.d.ts vendored
View File

@ -5,11 +5,18 @@ import { ICResponse } from "./messages/response.js";
* Contains methods to connect to and communicate with an IntelliCenter controller.
*
* Call `connect` to connect to the unit.
* Use `send` to send a message.
* Subscribe to events to process socket conditions, notify updates, and message responses (if not `await`ing the response)
*
* Available events:
*
* * `"response-{messageID}"` - fired once per message sent with `send()` where {messageID} is the ID specified in the {@linkcode ICRequest} given to `send()`
* * `"notify"` - fired when an update is available to a property previously subscribed to via a {@linkcode SubscribeToUpdates} request
* * `"close"` - fired any time the client is closed by any means (timeout, by request, error, etc.)
* * `"open"` - fired when the socket connects to the unit successfully
* * `"error"` - fired when the socket encounters an unrecoverable error and will close
* * `"timeout"` - fired when the socket has not received a ping response within the allowed threshold and will close
* * `"connected"` - fired when a connection has completed successfully
*/
export declare class Unit extends EventEmitter {
endpoint: string;

28
dist/unit.js vendored
View File

@ -6,11 +6,18 @@ const debugUnit = debug("ic:unit");
* Contains methods to connect to and communicate with an IntelliCenter controller.
*
* Call `connect` to connect to the unit.
* Use `send` to send a message.
* Subscribe to events to process socket conditions, notify updates, and message responses (if not `await`ing the response)
*
* Available events:
*
* * `"response-{messageID}"` - fired once per message sent with `send()` where {messageID} is the ID specified in the {@linkcode ICRequest} given to `send()`
* * `"notify"` - fired when an update is available to a property previously subscribed to via a {@linkcode SubscribeToUpdates} request
* * `"close"` - fired any time the client is closed by any means (timeout, by request, error, etc.)
* * `"open"` - fired when the socket connects to the unit successfully
* * `"error"` - fired when the socket encounters an unrecoverable error and will close
* * `"timeout"` - fired when the socket has not received a ping response within the allowed threshold and will close
* * `"connected"` - fired when a connection has completed successfully
*/
export class Unit extends EventEmitter {
endpoint;
@ -39,9 +46,13 @@ export class Unit extends EventEmitter {
this.client.on("error", (evt) => {
// todo: emit event so we can reconnect? auto reconnect?
debugUnit("error in websocket: $o", evt);
this.emit("error");
socketCleanup();
});
this.client.on("open", heartbeat);
this.client.on("open", () => {
this.emit("open");
heartbeat();
});
this.client.on("ping", heartbeat);
this.client.on("pong", heartbeat);
this.client.on("close", socketCleanup);
@ -55,13 +66,18 @@ export class Unit extends EventEmitter {
this.client?.once("open", resolve);
});
debugUnit("connected");
this.emit("connected");
}
/**
* Closes the connection to the unit.
*/
close() {
if (!this.client) {
return;
}
debugUnit("closing connection by request");
this.client?.close();
this.emit("close");
this.client.close();
}
socketCleanup = () => {
debugUnit("socket cleanup");
@ -81,6 +97,7 @@ export class Unit extends EventEmitter {
clearTimeout(this.pingTimeout);
this.pingTimeout = setTimeout(() => {
debugUnit("terminating connection due to heartbeat timeout");
this.emit("timeout");
this.client?.terminate();
this.socketCleanup();
}, this.pingInterval + 5000);
@ -101,9 +118,14 @@ export class Unit extends EventEmitter {
* @returns a promise that resolves into the {@linkcode ICResponse} with information about the request.
*/
async send(request) {
if (!this.client) {
return await new Promise(() => {
throw new Error("client not connected");
});
}
const payload = JSON.stringify(request);
debugUnit("sending message of length %d with id %s", payload.length, request.messageID);
this.client?.send(payload);
this.client.send(payload);
return await new Promise((resolve) => {
this.once(`response-${request.messageID}`, (resp) => {
debugUnit(" returning response to message %s", request.messageID);

2
dist/unit.js.map vendored
View File

@ -1 +1 @@
{"version":3,"file":"unit.js","sourceRoot":"","sources":["../unit.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"unit.js","sourceRoot":"","sources":["../unit.ts"],"names":[],"mappings":"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"}

85
example.ts Normal file
View File

@ -0,0 +1,85 @@
"use strict";
import { FindUnits, Unit } from "./index.js";
import { messages } from "./messages/messages.js";
console.log("searching...");
const f = new FindUnits();
const units = await f.searchAsync(1000);
f.close();
console.log("Discovered units:", units);
if (units.length === 0) {
throw new Error("no IntelliCenter units found, exiting.");
}
if (units.length > 1) {
throw new Error(
`found more than one IntelliCenter unit, unsure which one to use. ${JSON.stringify(units)}`,
);
}
const endpoint = units[0].addressStr;
const port = units[0].port;
// const endpoint = "10.0.0.41";
// const port = 6680;
console.log("connecting to intellicenter device at", endpoint, "port", port);
const unit = new Unit(endpoint, port);
await unit.connect();
console.log("connected");
unit.on("notify", (msg) => {
console.log("received notify:", msg);
});
console.log("subscribing for updates...");
let resp = await unit.send(messages.SubscribeToUpdates("B1202", "LOTMP"));
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Info request...");
resp = await unit.send(messages.GetSystemInformation());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Config request...");
resp = await unit.send(messages.GetSystemConfiguration());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Body Status request...");
resp = await unit.send(messages.GetBodyStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Chemical Status request...");
resp = await unit.send(messages.GetChemicalStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Heaters request...");
resp = await unit.send(messages.GetHeaters());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Schedule request...");
resp = await unit.send(messages.GetSchedule());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Circuit Status request...");
resp = await unit.send(messages.GetCircuitStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Setpoint request...");
// resp = await unit.send(messages.SetSetpoint("B1202", 97));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off pool...");
// resp = await unit.send(messages.SetObjectStatus("B1101", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off water feature...");
// resp = await unit.send(messages.SetObjectStatus("C0003", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Heatmode request...");
// resp = await unit.send(messages.SetHeatMode("B1202", true));
// console.log("got response:", JSON.stringify(resp, null, 2));
unit.close();

View File

@ -51,7 +51,12 @@ export class UnitInfo {
* * `"serverFound"` - fired immediately when an IntelliCenter unit has been located; receives a {@linkcode UnitInfo} argument
*/
export class FindUnits extends EventEmitter {
constructor() {
/**
* Creates a new finder.
*
* @param broadcastInterface the address of the interface to send the broadcast to. If not specified, will use system selection. Only necessary if you have more than one network adapter/interface and want to search on a specific one.
*/
constructor(public broadcastInterface?: string) {
super();
// construct mDNS packet to ping for intellicenter controllers
@ -76,6 +81,9 @@ export class FindUnits extends EventEmitter {
this.finder = createSocket("udp4");
this.finder
.on("listening", () => {
if (this.broadcastInterface) {
this.finder.setMulticastInterface(this.broadcastInterface);
}
this.finder.setBroadcast(true);
this.finder.setMulticastTTL(128);

View File

@ -1,82 +1,4 @@
"use strict";
import { FindUnits } from "./finder.js";
import { messages } from "./messages/messages.js";
import { Unit } from "./unit.js";
console.log("searching...");
const f = new FindUnits();
const units = await f.searchAsync(1000);
f.close();
console.log("Discovered units:", units);
if (units.length === 0) {
throw new Error("no IntelliCenter units found, exiting.");
}
if (units.length > 1) {
throw new Error(
`found more than one IntelliCenter unit, unsure which one to use. ${JSON.stringify(units)}`,
);
}
const endpoint = units[0].addressStr;
const port = units[0].port;
// const endpoint = "10.0.0.41";
// const port = 6680;
console.log("connecting to intellicenter device at", endpoint, "port", port);
const unit = new Unit(endpoint, port);
await unit.connect();
console.log("connected");
unit.on("notify", (msg) => {
console.log("received notify:", msg);
});
console.log("subscribing for updates...");
let resp = await unit.send(messages.SubscribeToUpdates("B1202", "LOTMP"));
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Info request...");
resp = await unit.send(messages.GetSystemInformation());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get System Config request...");
resp = await unit.send(messages.GetSystemConfiguration());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Body Status request...");
resp = await unit.send(messages.GetBodyStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Chemical Status request...");
resp = await unit.send(messages.GetChemicalStatus());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Heaters request...");
resp = await unit.send(messages.GetHeaters());
console.log("got response:", JSON.stringify(resp, null, 2));
console.log("sending Get Schedule request...");
resp = await unit.send(messages.GetSchedule());
console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Setpoint request...");
// resp = await unit.send(messages.SetSetpoint("B1202", 97));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off pool...");
// resp = await unit.send(messages.SetItemStatus("B1101", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("turning off water feature...");
// resp = await unit.send(messages.SetItemStatus("C0003", false));
// console.log("got response:", JSON.stringify(resp, null, 2));
// console.log("sending Set Heatmode request...");
// resp = await unit.send(messages.SetHeatMode("B1202", true));
// console.log("got response:", JSON.stringify(resp, null, 2));
unit.close();
export { FindUnits, Unit };

View File

@ -0,0 +1,39 @@
import { GetRequest, ICRequest, ICRequestObj } from "./request.js";
/**
* Requests the list of circuits known to this controller.
*
* The response contains an `objectList` populated with circuit information.
*
* @returns the object used to issue this request
*/
export function GetCircuitStatus(): ICRequest {
const req = GetRequest();
req.command = "GetParamList";
req.condition = "OBJTYP = CIRCUIT";
req.objectList = [];
const reqObj = new ICRequestObj();
reqObj.objnam = "ALL";
reqObj.keys = [
"OBJNAM",
"OBJTYP",
"SUBTYP",
"STATUS",
"BODY",
"SNAME",
"HNAME",
"FREEZE",
"DNTSTP",
"HNAME",
"TIME",
"FEATR",
"USAGE",
"LIMIT",
"USE",
"SHOMNU",
];
req.objectList.push(reqObj);
return req;
}

View File

@ -4,12 +4,12 @@ import { GetRequest, ICRequest } from "./request.js";
* Requests the configuration of bodies and circuits available to this controller.
*
* The response contains the list of bodies and circuits under the `answer` field.
* Each item has an `objnam` that should be used to reference that item for future requests,
* and `params`.`SNAME` is the user-entered friendly name that can be displayed for the item.
* `params`.`OBJTYP` will be either BODY or CIRCUIT depending on the item it's describing.
* Each object has an `objnam` that should be used to reference that object for future requests,
* and `params`.`SNAME` is the user-entered friendly name that can be displayed for the object.
* `params`.`OBJTYP` will be either BODY or CIRCUIT depending on the object it's describing.
*
* Some items, such as the Pool body, will have the `params`.`OBJLIST` array populated with
* a series of attached items such as a chlorinator device.
* Some objects, such as the Pool body, will have the `params`.`OBJLIST` array populated with
* a series of attached objects such as a chlorinator device.
*
* @returns the object used to issue this request
*/

View File

@ -1,23 +1,25 @@
import { GetBodyStatus } from "./body-status.js";
import { GetChemicalStatus } from "./chem-status.js";
import { GetCircuitStatus } from "./circuit-status.js";
import { GetSystemConfiguration } from "./configuration.js";
import { GetHeaters } from "./get-heater.js";
import { SubscribeToUpdates } from "./notify.js";
import { GetSchedule } from "./schedule.js";
import { SetHeatMode } from "./set-heater.js";
import { SetItemStatus } from "./set-status.js";
import { SetObjectStatus } from "./set-object-status.js";
import { SetSetpoint } from "./setpoint.js";
import { GetSystemInformation } from "./system-info.js";
export const messages = {
GetBodyStatus,
GetChemicalStatus,
GetCircuitStatus,
GetHeaters,
GetSchedule,
GetSystemConfiguration,
GetSystemInformation,
SetHeatMode,
SetItemStatus,
SetObjectStatus,
SetSetpoint,
SubscribeToUpdates,
};

View File

@ -2,28 +2,28 @@ import { ICParam } from "./param.js";
import { GetRequest, ICRequest, ICRequestObj } from "./request.js";
/**
* Requests to change the status of items known to this controller.
* Requests to change the status of objects known to this controller.
*
* Turns one or more items on or off. Use the `objnam` of the circuit to be set.
* Turns one or more objects on or off. Use the `objnam` of the circuit to be set.
*
* @returns the object used to issue this request
*/
export function SetItemStatus(
item: string | string[],
export function SetObjectStatus(
object: string | string[],
status: boolean,
): ICRequest {
const req = GetRequest();
req.command = "SetParamList";
req.objectList = [];
let items: string[];
if (Array.isArray(item)) {
items = item;
let objects: string[];
if (Array.isArray(object)) {
objects = object;
} else {
items = [item];
objects = [object];
}
for (const i of items) {
for (const i of objects) {
const reqObj = new ICRequestObj();
reqObj.objnam = i;
reqObj.params = new ICParam();

927
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "node-intellicenter",
"version": "0.0.1",
"version": "0.1.0",
"description": "NodeJS library for communicating with a Pentair IntelliCenter controller",
"keywords": [
"pentair",
@ -21,13 +21,18 @@
"license": "MIT",
"author": "Parnic",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./unit": "./dist/unit.js",
"./finder": "./dist/finder.js",
"./messages": "./dist/messages/messages.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"index": "tsc && node dist/index.js",
"lint": "eslint . && prettier . --check"
"example": "tsc && node dist/example.js",
"lint": "eslint . && prettier . --check && markdownlint-cli2 **/*.md",
"lint:fix": "eslint . --fix && prettier . --write && markdownlint-cli2 --fix **/*.md"
},
"dependencies": {
"debug": "^4.4.0",
@ -39,6 +44,7 @@
"@types/debug": "^4.1.12",
"@types/ws": "^8.5.13",
"eslint": "^9.17.0",
"markdownlint-cli2": "^0.17.1",
"prettier": "3.4.2",
"supports-color": "^10.0.0",
"typescript": "^5.7.2",

30
unit.ts
View File

@ -16,11 +16,18 @@ const debugUnit = debug("ic:unit");
* Contains methods to connect to and communicate with an IntelliCenter controller.
*
* Call `connect` to connect to the unit.
* Use `send` to send a message.
* Subscribe to events to process socket conditions, notify updates, and message responses (if not `await`ing the response)
*
* Available events:
*
* * `"response-{messageID}"` - fired once per message sent with `send()` where {messageID} is the ID specified in the {@linkcode ICRequest} given to `send()`
* * `"notify"` - fired when an update is available to a property previously subscribed to via a {@linkcode SubscribeToUpdates} request
* * `"close"` - fired any time the client is closed by any means (timeout, by request, error, etc.)
* * `"open"` - fired when the socket connects to the unit successfully
* * `"error"` - fired when the socket encounters an unrecoverable error and will close
* * `"timeout"` - fired when the socket has not received a ping response within the allowed threshold and will close
* * `"connected"` - fired when a connection has completed successfully
*/
export class Unit extends EventEmitter {
private client?: WebSocket;
@ -56,9 +63,13 @@ export class Unit extends EventEmitter {
this.client.on("error", (evt) => {
// todo: emit event so we can reconnect? auto reconnect?
debugUnit("error in websocket: $o", evt);
this.emit("error");
socketCleanup();
});
this.client.on("open", heartbeat);
this.client.on("open", () => {
this.emit("open");
heartbeat();
});
this.client.on("ping", heartbeat);
this.client.on("pong", heartbeat);
this.client.on("close", socketCleanup);
@ -75,14 +86,20 @@ export class Unit extends EventEmitter {
});
debugUnit("connected");
this.emit("connected");
}
/**
* Closes the connection to the unit.
*/
public close() {
if (!this.client) {
return;
}
debugUnit("closing connection by request");
this.client?.close();
this.emit("close");
this.client.close();
}
private socketCleanup = () => {
@ -108,6 +125,7 @@ export class Unit extends EventEmitter {
this.pingTimeout = setTimeout(() => {
debugUnit("terminating connection due to heartbeat timeout");
this.emit("timeout");
this.client?.terminate();
this.socketCleanup();
}, this.pingInterval + 5000);
@ -132,6 +150,12 @@ export class Unit extends EventEmitter {
* @returns a promise that resolves into the {@linkcode ICResponse} with information about the request.
*/
public async send(request: ICRequest): Promise<ICResponse> {
if (!this.client) {
return await new Promise(() => {
throw new Error("client not connected");
});
}
const payload = JSON.stringify(request);
debugUnit(
"sending message of length %d with id %s",
@ -139,7 +163,7 @@ export class Unit extends EventEmitter {
request.messageID,
);
this.client?.send(payload);
this.client.send(payload);
return await new Promise((resolve) => {
this.once(`response-${request.messageID}`, (resp: ICResponse) => {
debugUnit(" returning response to message %s", request.messageID);