Skip to content

Commit

Permalink
Initial update
Browse files Browse the repository at this point in the history
  • Loading branch information
pandizajner committed Jul 6, 2024
1 parent 6b24bc5 commit 64febc0
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 245 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"files.eol": "\n",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"editor.rulers": [ 140 ],
"eslint.enable": true
Expand Down
30 changes: 20 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
{
"private": true,
"displayName": "Plugin Name",
"name": "homebridge-plugin-name",
"displayName": "Homebridge Hon AC",
"name": "homebridge-hon-ac",
"version": "1.0.0",
"description": "A short description about what your plugin does.",
"license": "Apache-2.0",
"description": "Homebridge plugin for Haier AC using HON API",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/USERNAME/GITHUB_PROJECT_NAME.git"
"url": "https://github.com/pandizajner/homebridge-hon-ac.git"
},
"bugs": {
"url": "https://github.com/USERNAME/GITHUB_PROJECT_NAME/issues"
"url": "https://github.com/pandizajner/homebridge-hon-ac/issues"
},
"engines": {
"node": "^18.17.0 || ^20.9.0",
"homebridge": "^1.6.0"
},
"main": "dist/index.js",
"scripts": {
"lint": "eslint src/**/*.ts --max-warnings=0",
"watch": "npm run build && npm link && nodemon",
"build": "rimraf ./dist && tsc",
"lint": "eslint src --ext .ts",
"watch": "tsc -w",
"prepublishOnly": "npm run lint && npm run build"
},
"keywords": [
"homebridge-plugin"
"homebridge-plugin",
"homebridge",
"homekit",
"HON",
"Haier",
"AC"
],
"devDependencies": {
"@types/node": "^20.12.7",
Expand All @@ -36,5 +41,10 @@
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
}
},
"dependencies": {
"axios": "^0.21.1"
},
"author": "",
"homepage": "https://github.com/pandizajner/homebridge-hon-ac#readme"
}
23 changes: 23 additions & 0 deletions src/hon-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axios from 'axios';

export class HonAPI {
private email: string;
private password: string;

constructor(email: string, password: string) {
this.email = email;
this.password = password;
}

async getDevices() {
const response = await axios.post('https://geofence.haiersmart.com/v2/auth/login', {
email: this.email,
password: this.password,
});
const token = response.data.token;
const devicesResponse = await axios.get('https://geofence.haiersmart.com/v2/device', {
headers: { Authorization: `Bearer ${token}` },
});
return devicesResponse.data.devices;
}
}
8 changes: 2 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { API } from 'homebridge';

import { PLATFORM_NAME } from './settings';
import { ExampleHomebridgePlatform } from './platform';
import { HonACPlatform } from './platform';

/**
* This method registers the platform with Homebridge
*/
export = (api: API) => {
api.registerPlatform(PLATFORM_NAME, ExampleHomebridgePlatform);
api.registerPlatform(PLATFORM_NAME, HonACPlatform);
};
109 changes: 16 additions & 93 deletions src/platform.ts
Original file line number Diff line number Diff line change
@@ -1,120 +1,43 @@
import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge';

import { PLATFORM_NAME, PLUGIN_NAME } from './settings';
import { ExamplePlatformAccessory } from './platformAccessory';
import { HonACPlatformAccessory } from './platformAccessory';
import { HonAPI } from './hon-api';

/**
* 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 ExampleHomebridgePlatform implements DynamicPlatformPlugin {
export class HonACPlatform 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[] = [];
private readonly accessories: PlatformAccessory[] = [];

constructor(
public readonly log: Logger,
public readonly config: PlatformConfig,
public readonly api: API,
) {
this.log.debug('Finished initializing platform:', this.config.name);

// Homebridge 1.8.0 introduced a `log.success` method that can be used to log success messages
// For users that are on a version prior to 1.8.0, we need a 'polyfill' for this method
if (!log.success) {
log.success = log.info;
}

// 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', () => {
log.debug('Executed didFinishLaunching callback');
// run the method to discover / register your devices as accessories
this.discoverDevices();
});
this.api = new HonAPI(this.config.email, this.config.password);
this.api.on('didFinishLaunching', this.discoverDevices.bind(this));
}

/**
* This function is invoked when homebridge restores cached accessories from disk at startup.
* It should be used to set up 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() {

// 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 = [
{
exampleUniqueId: 'ABCD',
exampleDisplayName: 'Bedroom',
},
{
exampleUniqueId: 'EFGH',
exampleDisplayName: 'Kitchen',
},
];

// 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.exampleUniqueId);

// 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
async discoverDevices() {
const devices = await this.api.getDevices();
for (const device of devices) {
this.log.info('Found device:', device.name);
const uuid = this.api.hap.uuid.generate(device.id);
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`. e.g.:
// 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);

// it is possible to remove platform accessories at any time using `api.unregisterPlatformAccessories`, e.g.:
// 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);
this.log.info('Restoring existing accessory:', existingAccessory.displayName);
new HonACPlatformAccessory(this, existingAccessory, device);
} else {
// the accessory does not yet exist, so we need to create it
this.log.info('Adding new accessory:', device.exampleDisplayName);

// create a new accessory
const accessory = new this.api.platformAccessory(device.exampleDisplayName, 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);

// link the accessory to your platform
this.log.info('Adding new accessory:', device.name);
const accessory = new this.api.platformAccessory(device.name, uuid);
new HonACPlatformAccessory(this, accessory, device);
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
}
}
Expand Down
141 changes: 15 additions & 126 deletions src/platformAccessory.ts
Original file line number Diff line number Diff line change
@@ -1,141 +1,30 @@
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
import { HonACPlatform } from './platform';

import { ExampleHomebridgePlatform } from './platform';

/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
export class ExamplePlatformAccessory {
export class HonACPlatformAccessory {
private service: Service;

/**
* These are just used to create a working example
* You should implement your own code to track the state of your accessory
*/
private exampleStates = {
On: false,
Brightness: 100,
};

constructor(
private readonly platform: ExampleHomebridgePlatform,
private readonly platform: HonACPlatform,
private readonly accessory: PlatformAccessory,
private readonly device: any,

Check failure on line 10 in src/platformAccessory.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type

Check failure on line 10 in src/platformAccessory.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Unexpected any. Specify a different type
) {
this.service = this.accessory.getService(this.platform.Service.Thermostat) ||
this.accessory.addService(this.platform.Service.Thermostat);
this.service.setCharacteristic(this.platform.Characteristic.Name, device.name);

// set accessory information
this.accessory.getService(this.platform.Service.AccessoryInformation)!
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Default-Manufacturer')
.setCharacteristic(this.platform.Characteristic.Model, 'Default-Model')
.setCharacteristic(this.platform.Characteristic.SerialNumber, 'Default-Serial');

// get the LightBulb service if it exists, otherwise create a new LightBulb service
// you can create multiple services for each accessory
this.service = this.accessory.getService(this.platform.Service.Lightbulb) || this.accessory.addService(this.platform.Service.Lightbulb);

// set the service name, this is what is displayed as the default name on the Home app
// in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method.
this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.exampleDisplayName);

// each service must implement at-minimum the "required characteristics" for the given service type
// see https://developers.homebridge.io/#/service/Lightbulb

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

// register handlers for the Brightness Characteristic
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
.onSet(this.setBrightness.bind(this)); // SET - bind to the 'setBrightness` method below

/**
* Creating multiple services of the same type.
*
* To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
* when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
* this.accessory.getService('NAME') || this.accessory.addService(this.platform.Service.Lightbulb, 'NAME', 'USER_DEFINED_SUBTYPE_ID');
*
* The USER_DEFINED_SUBTYPE must be unique to the platform accessory (if you platform exposes multiple accessories, each accessory
* can use the same subtype id.)
*/

// Example: add two "motion sensor" services to the accessory
const motionSensorOneService = this.accessory.getService('Motion Sensor One Name') ||
this.accessory.addService(this.platform.Service.MotionSensor, 'Motion Sensor One Name', 'YourUniqueIdentifier-1');

const motionSensorTwoService = this.accessory.getService('Motion Sensor Two Name') ||
this.accessory.addService(this.platform.Service.MotionSensor, 'Motion Sensor Two Name', 'YourUniqueIdentifier-2');

/**
* Updating characteristics values asynchronously.
*
* Example showing how to update the state of a Characteristic asynchronously instead
* of using the `on('get')` handlers.
* Here we change update the motion sensor trigger states on and off every 10 seconds
* the `updateCharacteristic` method.
*
*/
let motionDetected = false;
setInterval(() => {
// EXAMPLE - inverse the trigger
motionDetected = !motionDetected;
this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
.onGet(this.getCurrentTemperature.bind(this));

// push the new value to HomeKit
motionSensorOneService.updateCharacteristic(this.platform.Characteristic.MotionDetected, motionDetected);
motionSensorTwoService.updateCharacteristic(this.platform.Characteristic.MotionDetected, !motionDetected);

this.platform.log.debug('Triggering motionSensorOneService:', motionDetected);
this.platform.log.debug('Triggering motionSensorTwoService:', !motionDetected);
}, 10000);
this.service.getCharacteristic(this.platform.Characteristic.TargetTemperature)
.onSet(this.setTargetTemperature.bind(this));
}

/**
* Handle "SET" requests from HomeKit
* These are sent when the user changes the state of an accessory, for example, turning on a Light bulb.
*/
async setOn(value: CharacteristicValue) {
// implement your own code to turn your device on/off
this.exampleStates.On = value as boolean;

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

/**
* Handle the "GET" requests from HomeKit
* These are sent when HomeKit wants to know the current state of the accessory, for example, checking if a Light bulb is on.
*
* GET requests should return as fast as possible. A long delay here will result in
* HomeKit being unresponsive and a bad user experience in general.
*
* If your device takes time to respond you should update the status of your device
* asynchronously instead using the `updateCharacteristic` method instead.
* @example
* this.service.updateCharacteristic(this.platform.Characteristic.On, true)
*/
async getOn(): Promise<CharacteristicValue> {
// implement your own code to check if the device is on
const isOn = this.exampleStates.On;

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

// if you need to return an error to show the device as "Not Responding" in the Home app:
// throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);

return isOn;
async getCurrentTemperature(): Promise<CharacteristicValue> {
return this.device.currentTemperature;
}

/**
* Handle "SET" requests from HomeKit
* These are sent when the user changes the state of an accessory, for example, changing the Brightness
*/
async setBrightness(value: CharacteristicValue) {
// implement your own code to set the brightness
this.exampleStates.Brightness = value as number;

this.platform.log.debug('Set Characteristic Brightness -> ', value);
async setTargetTemperature(value: CharacteristicValue) {
this.device.setTargetTemperature(value);
}

}
Loading

0 comments on commit 64febc0

Please sign in to comment.