Skip to content

Commit

Permalink
Clean up and use config values
Browse files Browse the repository at this point in the history
  • Loading branch information
OMikkel committed Dec 17, 2023
1 parent 96dacc7 commit 91573d5
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 138 deletions.
37 changes: 29 additions & 8 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"level": {
"title": "Access Level",
"type": "string",
"enum": ["treeview", "openapi", "administator"]
"enum": ["treeview", "openapi", "administrator"]
}
}
},
Expand All @@ -38,17 +38,38 @@
"properties": {
"name": {
"title": "Device Name",
"type": "string"
"type": "string",
"placeholder": "Ceiling Light"
},
"id": {
"title": "Device ID",
"type": "string"
},
"type": {
"title": "Device Type",
"type": "string",
"enum": ["light", "switch", "blind", "scene"]
}
"placeholder": "2300178"
},
"type": {
"title": "Device Type",
"type": "string",
"enum": ["light", "switch"]
},
"metadata": {
"title": "Metadata",
"type": "object",
"properties": {
"manufacturer": {
"title": "Manufacturer",
"type": "string",
"placeholder": "LK IHC"
},
"model": {
"title": "Model",
"type": "string"
},
"serial": {
"title": "Serial Number",
"type": "string"
}
}
}
}
}
}
Expand Down
203 changes: 73 additions & 130 deletions src/platform.ts
Original file line number Diff line number Diff line change
@@ -1,148 +1,91 @@
import {
API,
DynamicPlatformPlugin,
Logger,
PlatformAccessory,
PlatformConfig,
Service,
Characteristic,
API,
Characteristic,
DynamicPlatformPlugin,
Logger,
PlatformAccessory,
PlatformConfig,
Service,
} from "homebridge";

import { PLATFORM_NAME, PLUGIN_NAME } from "./settings";
import { ExamplePlatformAccessory } from "./platformAccessory";
import { authenticate } from "./lib/authenticate";
import { SOAPRequest } from "./lib/request";
import { PLATFORM_NAME, PLUGIN_NAME } from "./settings";
import { BasePlatformAccessory } from "./platforms/index"

Check warning on line 13 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing semicolon

Check warning on line 13 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Missing semicolon

/**
* HomebridgePlatform
* This class is the main constructor for your plugin, this is where you should
* parse the user config and discover/register accessories with Homebridge.
*/
export class HomebridgeIHC implements DynamicPlatformPlugin {
public readonly Service: typeof Service = this.api.hap.Service;
public readonly Characteristic: typeof Characteristic =
this.api.hap.Characteristic;

// this is used to track restored cached accessories
public readonly accessories: PlatformAccessory[] = [];

constructor(
public readonly log: Logger,
public readonly config: PlatformConfig,
public readonly api: API
public readonly Service: typeof Service = this.api.hap.Service;
public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic;
public readonly accessories: PlatformAccessory[] = [];

constructor(
public readonly log: Logger,
public readonly config: PlatformConfig,
public readonly api: API
) {
const request = new SOAPRequest(config.endpoint)

Check warning on line 25 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing semicolon

Check warning on line 25 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Missing semicolon
this.log.debug("Finished initializing platform:", this.config.name);

// When this event is fired it means Homebridge has restored all cached accessories from disk.
// Dynamic Platform plugins should only register new accessories after this event was fired,
// in order to ensure they weren't added to homebridge already. This event can also be used
// to start discovery of new accessories.
this.api.on("didFinishLaunching", async () => {
this.api.on("didFinishLaunching", async () => {
log.debug("Executed didFinishLaunching callback");

const authenticated = await request.authenticate(config.auth)

Check warning on line 31 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing semicolon

Check warning on line 31 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Missing semicolon
if (authenticated) {
this.discoverDevices(request);
} else {
this.log.warn("Terminating - Invalid user credentials")

Check warning on line 35 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing semicolon

Check warning on line 35 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Missing semicolon
}
});
}

/**
* This function is invoked when homebridge restores cached accessories from disk at startup.
* It should be used to setup event handlers for characteristics and update respective values.
*/
configureAccessory(accessory: PlatformAccessory) {
this.log.info("Loading accessory from cache:", accessory.displayName);

// add the restored accessory to the accessories cache so we can track if it has already been registered
this.accessories.push(accessory);
}

/**
* This is an example method showing how to register discovered accessories.
* Accessories must only be registered once, previously created accessories
* must not be registered again to prevent "duplicate UUID" errors.
*/
discoverDevices(request: SOAPRequest) {
// EXAMPLE ONLY
// A real plugin you would discover accessories from the local network, cloud services
// or a user-defined array in the platform config.
const exampleDevices = [
{
type: "light",
id: "5830162",
name: "Mikkels Værelse",
},
{
type: "light",
id: "7905371",
name: "Stue Loftudtag",
},
{
type: "light",
id: "2300178",
name: "Stue Vest Vindue",
}
];

// loop over the discovered devices and register each one if it has not already been registered
for (const device of exampleDevices) {
// generate a unique id for the accessory this should be generated from
// something globally unique, but constant, for example, the device serial
// number or MAC address
const uuid = this.api.hap.uuid.generate(device.id);

// see if an accessory with the same uuid has already been registered and restored from
// the cached devices we stored in the `configureAccessory` method above
const existingAccessory = this.accessories.find(
(accessory) => accessory.UUID === uuid
);

if (existingAccessory) {
// the accessory already exists
this.log.info(
"Restoring existing accessory from cache:",
existingAccessory.displayName
);

// if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
// existingAccessory.context.device = device;
// this.api.updatePlatformAccessories([existingAccessory]);

// create the accessory handler for the restored accessory
// this is imported from `platformAccessory.ts`
new ExamplePlatformAccessory(this, existingAccessory, request);

// it is possible to remove platform accessories at any time using `api.unregisterPlatformAccessories`, eg.:
// remove platform accessories when no longer present
// this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]);
// this.log.info('Removing existing accessory from cache:', existingAccessory.displayName);
} else {
// the accessory does not yet exist, so we need to create it
this.log.info("Adding new accessory:", device.name);

// create a new accessory
const accessory = new this.api.platformAccessory(
device.name,
uuid
);

// store a copy of the device object in the `accessory.context`
// the `context` property can be used to store any data about the accessory you may need
accessory.context.device = device;

// create the accessory handler for the newly create accessory
// this is imported from `platformAccessory.ts`
new ExamplePlatformAccessory(this, accessory, request);

// link the accessory to your platform
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [
accessory,
]);
}
}
}
});
}

configureAccessory(accessory: PlatformAccessory) {
this.log.info("Loading accessory from cache:", accessory.displayName);

this.accessories.push(accessory);
}

discoverDevices(request: SOAPRequest) {
const devices = this.config.devices

Check warning on line 47 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing semicolon

Check warning on line 47 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Missing semicolon

for (const device of devices) {
const uuid = this.api.hap.uuid.generate(device.id);

const existingAccessory = this.accessories.find((accessory) => accessory.UUID === uuid);

if (existingAccessory) {
this.log.info("Restoring existing accessory from cache:", existingAccessory.displayName);

// switch (existingAccessory.context.device.type) {
// case "light":
// new LightPlatformAccessory(this, existingAccessory, request)
// break

// case "switch":
// new SwitchPlatformAccessory(this, existingAccessory, request)
// break
// }

new BasePlatformAccessory(this, existingAccessory, request)

Check warning on line 67 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing semicolon

Check warning on line 67 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Missing semicolon
} else {
this.log.info("Adding new accessory:", device.name);

const accessory = new this.api.platformAccessory(device.name, uuid);

accessory.context.device = device;

// switch (accessory.context.device.type) {
// case "light":
// new LightPlatformAccessory(this, accessory, request)
// break

// case "switch":
// new SwitchPlatformAccessory(this, accessory, request)
// break
// }

new BasePlatformAccessory(this, accessory, request)

Check warning on line 85 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing semicolon

Check warning on line 85 in src/platform.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Missing semicolon

this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
}
}
}
}
59 changes: 59 additions & 0 deletions src/platforms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
import { HomebridgeIHC } from "../platform"
import { SOAPRequest } from '../lib/request';

type State = {
value: boolean,
typeString: string,
resourceID: number,
isValueRuntime: boolean
}

export class BasePlatformAccessory {
service: Service;
state: State = {
value: false,
typeString: "",
resourceID: -1,
isValueRuntime: false
}

constructor(
private readonly platform: HomebridgeIHC,
private readonly accessory: PlatformAccessory,
private readonly request: SOAPRequest
) {
this.request = request
const accessoryService = this.accessory.getService(this.platform.Service.AccessoryInformation)!
accessoryService.setCharacteristic(this.platform.Characteristic.Manufacturer, this.accessory.context?.metadata?.manufacturer || "LK IHC")
if (this.accessory.context?.metadata?.model) accessoryService.setCharacteristic(this.platform.Characteristic.Model, this.accessory.context?.metadata?.model)
if (this.accessory.context?.metadata?.serial) accessoryService.setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context?.metadata?.serial)

this.service = this.accessory.getService(this.accessory.context.device.type == "light" ? this.platform.Service.Lightbulb : this.platform.Service.Switch) || this.accessory.addService(this.accessory.context.device.type == "light" ? this.platform.Service.Lightbulb : this.platform.Service.Switch);
this.service.setCharacteristic(this.platform.Characteristic.Name, this.accessory.context.device?.name || "Unknown device");

this.service.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setState.bind(this)) // SET - bind to the `setOn` method below
.onGet(this.getState.bind(this)); // GET - bind to the `getOn` method below

this.getState()
}

async setState(value: CharacteristicValue) {
this.state.value = value as boolean;

await this.request.setResourceValue(this.state, value as boolean)

this.platform.log.debug('Set Characteristic On ->', value);
}

async getState() {
this.state = await this.request.getResourceValue(this.accessory.context.device.id)

this.platform.log.debug("Light ID: ", this.accessory.context.device.id)

this.platform.log.debug('Get Characteristic On ->', this.state.value);

return this.state.value;
}
}
16 changes: 16 additions & 0 deletions src/platforms/light.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PlatformAccessory } from 'homebridge';
import { SOAPRequest } from '../lib/request';
import { HomebridgeIHC } from "../platform";
import { BasePlatformAccessory } from "./index";

export class LightPlatformAccessory extends BasePlatformAccessory {
constructor(
platform: HomebridgeIHC,
accessory: PlatformAccessory,
request: SOAPRequest
) {
super(platform, accessory, request)

this.service = accessory.getService(platform.Service.Lightbulb) || accessory.addService(platform.Service.Lightbulb);
}
}
16 changes: 16 additions & 0 deletions src/platforms/switch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PlatformAccessory } from 'homebridge';
import { SOAPRequest } from '../lib/request';
import { HomebridgeIHC } from "../platform";
import { BasePlatformAccessory } from "./index";

export class SwitchPlatformAccessory extends BasePlatformAccessory {
constructor(
platform: HomebridgeIHC,
accessory: PlatformAccessory,
request: SOAPRequest
) {
super(platform, accessory, request)

this.service = accessory.getService(platform.Service.Switch) || accessory.addService(platform.Service.Switch);
}
}

0 comments on commit 91573d5

Please sign in to comment.