Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lawrencebensaid committed Mar 16, 2021
0 parents commit c75c185
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
package-lock.json
dist
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# eNet Homebridge plugin

The accessories added to eNet smart home will automatically appear in the Apple Home app if the below configuratino is correct.

## Configuration

```
{
"platform": "eNetPlatform",
"username": "admin",
"password": "admin",
"host": "192.168.0.7"
}
```
37 changes: 37 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "homebridge-enet",
"version": "1.0.0",
"description": "ENet implementation for Apple HomeKit",
"main": "dist/dynamic-platform.js",
"scripts": {
"clean": "rm -rf ./dist",
"build": "rm -rf ./dist && tsc"
},
"author": "Lawrence Bensaid <[email protected]>",
"engines": {
"homebridge": ">=1.0.0"
},
"keywords": [
"homebridge-plugin"
],
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "git://github.com/lawrencebensaid/homebridge-enet.git"
},
"bugs": {
"url": "http://github.com/lawrencebensaid/homebridge-enet/issues"
},
"devDependencies": {
"@types/node": "^10.17.19",
"homebridge": "^1.3.3",
"rimraf": "^3.0.2",
"typescript": "^3.9.9"
},
"dependencies": {
"enet-api": "^1.2.1",
"request": "^2.88.2"
}
}
178 changes: 178 additions & 0 deletions src/dynamic-platform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { ENet } from "enet-api";
import fs from "fs";
import {
API,
CharacteristicEventTypes,
CharacteristicGetCallback,
CharacteristicSetCallback,
CharacteristicValue,
DynamicPlatformPlugin,
HAP,
Logging,
PlatformAccessory,
PlatformAccessoryEvent,
PlatformConfig
} from "homebridge";
const { version } = require("../package.json");

const PLUGIN_NAME = "homebridge-enet";
const PLATFORM_NAME = "eNetPlatform";

let hap: HAP;
let Accessory: typeof PlatformAccessory;

export = (api: API) => {
hap = api.hap;
Accessory = api.platformAccessory;

api.registerPlatform(PLATFORM_NAME, ENetPlatform);
};

class ENetPlatform implements DynamicPlatformPlugin {

private readonly log: Logging;
private readonly api: API;
private readonly config: PlatformConfig;
private enet: ENet;

private readonly accessories: PlatformAccessory[] = [];
private devices: { [id: string]: any } = {};

constructor(log: Logging, config: PlatformConfig, api: API) {
this.log = log;
this.api = api;
this.config = config;

this.log.info(`eNet start initializing!`);

const path = `${this.api.user.storagePath()}/.enet`;
if (!fs.existsSync(path)) {
fs.writeFileSync(path, JSON.stringify({ token: null }), { encoding: "utf8" });
}
var token = null;
try {
token = JSON.parse(fs.readFileSync(path, { encoding: "utf8" })).token;
} catch (error) { }

if (token === null) {
this.enet = new ENet(config.host);
this.log.info("Authentication needed!");
this.enet.authenticate(this.config.username, this.config.password)
.then((token: string) => {
fs.writeFileSync(path, JSON.stringify({ token }), { encoding: "utf8" });
this.setupNow();
})
.catch((error: Error) => {
this.log.error(error.message);
})
} else {
this.enet = new ENet(config.host, token);
this.setupNow();
}

// Handle polling
const interval: number = typeof config.interval === "number" ? config.interval : 10;
if (interval >= 3) {
setInterval(() => {
this.poll();
}, interval * 1000)
}

}

setupNow() {
return new Promise<void>(async (resolve, reject) => {
try {
const devices = await this.enet.getDevices();

this.log.info(devices);

for (let device of devices) {
const uuid = device.deviceUID.toLowerCase();
delete device.deviceUID;
this.devices[uuid] = device;
this.devices[uuid].state = null;
if (!this.accessories.find(accessory => accessory.UUID === uuid)) {
const accessory = new this.api.platformAccessory(device.locationName, uuid);
accessory.addService(hap.Service.Switch, device.locationName);
this.accessories.push(accessory);
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
}
}

resolve();
} catch (error) {
reject(error.message);
}

});
}


poll() {
for (const deviceUID in this.devices) {
this.enet.getDevicePrimaryState(deviceUID).then((res: any) => {
this.devices[deviceUID].state = res;
});
}
}


configureAccessory(accessory: PlatformAccessory): void {

(async () => {

this.log.info(`Configuring '${accessory.UUID}'`);

const device = await this.enet.getDeviceInfo(accessory.UUID);

accessory.on(PlatformAccessoryEvent.IDENTIFY, () => {
this.log(`${accessory.displayName} identified!`);
});

const info = accessory.getService(hap.Service.AccessoryInformation);
if (info) {
info.setCharacteristic(hap.Characteristic.Model, device.typeID);
info.setCharacteristic(hap.Characteristic.SerialNumber, device.metaData.serialNumber);
info.setCharacteristic(hap.Characteristic.FirmwareRevision, version);
info.setCharacteristic(hap.Characteristic.FirmwareUpdateStatus, device.isSoftwareUpdateAvailable ? "Update available" : "Up to date");
info.setCharacteristic(hap.Characteristic.Manufacturer, "Insta GmbH");
info.setCharacteristic(hap.Characteristic.AppMatchingIdentifier, "de.insta.enet.smarthome");
}

const service = accessory
.getService(hap.Service.Switch)!;

service
.getCharacteristic(hap.Characteristic.On)
.on(CharacteristicEventTypes.SET, async (value: CharacteristicValue, callback: CharacteristicSetCallback) => {

try {
await this.enet.setDevicePrimaryState(accessory.UUID, value);
callback();
} catch (error) {
this.log.info(error.message);
}

});

service
.getCharacteristic(hap.Characteristic.On)
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
const device = this.devices[accessory.UUID.toLowerCase()];
if (device) {
callback(null, this.devices[accessory.UUID].state === true);
} else {
this.log.error("device", accessory.UUID, "not found");
callback(null, false);
}
});


this.accessories.push(accessory);

})();

}

}
22 changes: 22 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"lib": [
"ES2015",
"ES2016",
"ES2017",
"ES2018"
],
"sourceMap": true,
"rootDir": "src",
"outDir": "dist",

"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"src"
]
}

0 comments on commit c75c185

Please sign in to comment.