Skip to content

Commit

Permalink
Support VR ipx800V4 + poll interval + url force refresh (#1)
Browse files Browse the repository at this point in the history
* begin get vr pos

* add poll interval

* add webhook handler

* add install script

* bugfix

* fix

* test

* test

* same

* test

* add more log

* fix

* add log

* add log

* add log

* test

* test

* test

* add log

* typoi

* test

* fix

* remove log

* try

* typo

* remove log

* add log

* fix log

* test

* test

* bugfix

* test

* test real time update position

* typo

* bugfix

* test

* same

* test

* add log

* test

* add log

* bugfix

* bugfix

* bugfix

* bugfix

* test

* test

* test

* test

* test

* test

* improvement

* improvement

* update readme

* improve chache restore

* add more log

* test

* test

* bugfix

* test

* add more log

* test

* test

* add log

* test

* test

* remove log

* test

* test

* revert

* remove const

* add more config

* test

* test

* fix

* typo

* add log

* change log level

* remove log

* improve code

* add name in place of model

* Update gradual.ts

* Update package.json

* Update install.sh

* Update install.sh

* Update install.sh

* Update install.sh

* bugfix

* bugfix

* test

* test

* test

* remove get (not usefull)

* test

* test verify result from ipx

* bugfix

* same

* bugfix

* test

* test simulate error

* typo debug log

* bugfix

* remove simulation of error

* Update ipxV4.ts

* add more log

* Update ipxV4.ts

* Update ipxV4.ts

* typo

* add more log

* Update ipxV4.ts

* test

* test

* bugfix

* test

* test

* test

* fix

* typo

* fix

* test

* test

* test

* add more log

* add more delay

* test

* test

* test

* test

* bugfix

* test

* same

* test

* test

* test

* test

* typo

* improvements

* test

* add log

* reduce ipx call

* test

* test

* test

* test

* fix

* test

* test

* test

* fix

* typo

* test

* improve code

* update readme

* test

* add verfiy on vr

* test

* bugfix

* improve timming
  • Loading branch information
zoic21 authored Jan 19, 2025
1 parent 57047b7 commit 5d4f767
Show file tree
Hide file tree
Showing 17 changed files with 364 additions and 175 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
47 changes: 21 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@

<p align="center">

<!-- <img src="https://github.com/homebridge/branding/raw/master/logos/homebridge-wordmark-logo-vertical.png" width="150"> -->
<img src="https://play-lh.googleusercontent.com/9doSEOplvz2nX67aaO6mCUc-UpcqQSEIalcfookSB0tR2QA6F9ffMv1QKcHWfWp3kjim" width="250">

</p>


# Homebridge IPX800 Plugin

This plugin brings support of IPX800 to homekit.
Expand All @@ -18,14 +9,12 @@ As now it support different devices :
* analog inputs (ipx, x-thl)
2. ipx v4
* relays
* gradual (x-dimmer, x4Vr) without state update
* gradual (x-dimmer, x4Vr)
* analog inputs (ipx, x-thl)


This is heavily based on the hombridge plateform template it may let you control your ipx800 relays.



## Install Development Dependencies

Using a terminal on the computer running homebridge:
Expand All @@ -34,39 +23,38 @@ Using a terminal on the computer running homebridge:
#clone plugin
git clone https://github.com/Adrien-B/ipx800.git
# install dependency
cd ipx800
npm install
sudo npm install -g typescript rimraf
# build and link plugin
npm run build
npm link #or sudo npm link
Chmod +x install.sh;./install.sh
#(re)start homebridge if not done already
homebridge -D
```


## Configure the plugin

In homebridge set the ipx api settings
* ip
* api-key
* version
* pollInterval
* webhookPort
* webhookPath

See the following json snippet exemple: 
```
"api": {
"ip": "*.*.*.*",
"key": "*",
"version": "v5"
},
"ip": "*.*.*.*",
"key": "*",
"version": "v5",
"pollInterval": 60,
"webhookPort": 58698,
"webhookPath": "TODO"
},
```

After this configuration you can trigger a refresh of state by calling https://IP_HOMEBRIDGE:webhookPort/webhookPath, you can for exemple add this in ipx800v4 push to trigger an update when relay state change

### Configure v5 devices
Than add all your devices (relays, dimmer, inputs).
See the following json snippet exemple for v5: 
Expand Down Expand Up @@ -128,6 +116,13 @@ See the following json snippet exemple for v5: 
"index": "r3"
}
],
"graduals": [
{
"displayName": "corridor",
"type": "covering",
"anaIndex": "VR02"
}
],
"analogInputs": [
{
"displayName": "séjour",
Expand Down
11 changes: 11 additions & 0 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@
"default": "v4",
"enum": ["v4", "v5"],
"required": true
},
"pollInterval": {
"type": "number",
"default": 60
},
"webhookPort": {
"type": "number"
},
"webhookPath": {
"type": "string",
"default": ""
}
}
},
Expand Down
8 changes: 8 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
git pull
npm install
npm run build
npm link
npm pack
mv homebridge-ipx800-1.0.0.tgz /homebridge
cd /homebridge
npm install homebridge-ipx800-1.0.0.tgz
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/Adrien-B/ipx800"
"url": "https://github.com/zoic21/homebridge-ipx800.git"
},
"bugs": {
"url": "https://github.com/Adrien-B/ipx800/issues"
Expand Down
4 changes: 3 additions & 1 deletion src/conf-definition/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ export interface Api {
ip: string;
key: string;
version: "v4" | "v5";

pollInterval: number;
webhookPort: number;
webhookPath: string;
}
3 changes: 2 additions & 1 deletion src/conf-definition/relays.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ export type OutletFrPrise = "outlet";
export type FanFrVentilation = "fan";
export type SwitchFrInterrupteur = "switch";
export type ButtonFrPoussoir = "bswitch";
export type ToggleFrPoussoir = "toggle";
export type Valve = "valve";
export type Relays = {
displayName: string;
type: (LightFrLumiere | OutletFrPrise | FanFrVentilation | SwitchFrInterrupteur | ButtonFrPoussoir | Valve) & string;
type: (LightFrLumiere | OutletFrPrise | FanFrVentilation | SwitchFrInterrupteur | ButtonFrPoussoir | Valve | ToggleFrPoussoir) & string;
index: string;

};
6 changes: 6 additions & 0 deletions src/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

export type IpxIp = string;
export type IpxApiKey = string;
export type IpxPollInterval = number;
export type IpxWebhookPort = number;
export type IpxWebhookPath = string;
export type IpxVersion = IpxVersion1 & IpxVersion2;
export type IpxVersion1 = V4 | V5;
export type V4 = "v4";

Check warning on line 15 in src/config.d.ts

View workflow job for this annotation

GitHub Actions / build (12.x)

Strings must use singlequote

Check warning on line 15 in src/config.d.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Strings must use singlequote

Check warning on line 15 in src/config.d.ts

View workflow job for this annotation

GitHub Actions / build (14.x)

Strings must use singlequote

Check warning on line 15 in src/config.d.ts

View workflow job for this annotation

GitHub Actions / build (15.x)

Strings must use singlequote

Check warning on line 15 in src/config.d.ts

View workflow job for this annotation

GitHub Actions / build (13.x)

Strings must use singlequote
Expand All @@ -17,5 +20,8 @@ export interface IpxApiConfiguration {
ip?: IpxIp;
key?: IpxApiKey;
version?: IpxVersion;
pollInterval?: IpxPollInterval;
webhookPort?: IpxWebhookPort;
webhookPath?: IpxWebhookPath;
[k: string]: unknown;
}
14 changes: 7 additions & 7 deletions src/device/analogInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class AnalogInputHandler {
// set accessory information
this.accessory.getService(this.platform.Service.AccessoryInformation)!
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'GCE-Electronic')
.setCharacteristic(this.platform.Characteristic.Model, this.platform.model);
.setCharacteristic(this.platform.Characteristic.Model, accessory.context.device.displayName);

switch(accessory.context.device.type) {
case 'humidity': {
Expand All @@ -39,12 +39,12 @@ export class AnalogInputHandler {
this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.displayName);
}

async updateAnaValue(state: number){
async updateAnaValue(value: number){
if (this.characteristic === this.platform.Characteristic.CurrentAmbientLightLevel) {
state = Math.max(state, 0.1);
}// else if (this.index == "THL1-TEMP") {
// state = (state - 1);
// }
this.service.updateCharacteristic(this.characteristic, state);
value = Math.max(value, 0.1);
}
if(this.service.getCharacteristic(this.characteristic).value != value){
this.service.updateCharacteristic(this.characteristic, value);
}
}
}
30 changes: 21 additions & 9 deletions src/device/gradual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class GradualHandler {
public readonly anaIndex: string = this.accessory.context.device.anaIndex;
private service: Service;
private readonly characteristic;
private state;

constructor(
private readonly platform: IPXPlatform,
Expand All @@ -18,15 +19,13 @@ export class GradualHandler {
// set accessory information
this.accessory.getService(this.platform.Service.AccessoryInformation)!
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'GCE-Electronic')
.setCharacteristic(this.platform.Characteristic.Model, this.platform.model);
.setCharacteristic(this.platform.Characteristic.Model, accessory.context.device.displayName);

switch(accessory.context.device.type) {
case 'covering': {
this.service = this.accessory.getService(this.platform.Service.WindowCovering) ||
this.accessory.addService(this.platform.Service.WindowCovering);
this.service = this.accessory.getService(this.platform.Service.WindowCovering) || this.accessory.addService(this.platform.Service.WindowCovering);
this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.displayName);
this.service.getCharacteristic(this.platform.Characteristic.TargetPosition)
.onSet((v) => ipx.setVRPosition(v, this.platform, this.accessory));
this.service.getCharacteristic(this.platform.Characteristic.TargetPosition).onSet((v) => ipx.setVRPosition(v, this.platform, this.accessory));
this.characteristic = this.platform.Characteristic.CurrentPosition; //but onSet is TargetPosition
break;
}
Expand All @@ -51,14 +50,27 @@ export class GradualHandler {

async updateAnaValue(value: number){
if (this.characteristic === this.platform.Characteristic.CurrentPosition) {
//if curtain (xv4r) revert position
this.service.updateCharacteristic(this.characteristic, 100 - value);
this.state = 100 - value;
if(this.service.getCharacteristic(this.characteristic).value != (100 - value)){
this.service.updateCharacteristic(this.characteristic, 100 - value);
this.service.updateCharacteristic(this.platform.Characteristic.PositionState, this.platform.Characteristic.PositionState.STOPPED);
this.service.updateCharacteristic(this.platform.Characteristic.TargetPosition, 100 - value);
}
if(this.service.getCharacteristic(this.platform.Characteristic.PositionState).value != this.platform.Characteristic.PositionState.STOPPED){
this.service.updateCharacteristic(this.platform.Characteristic.PositionState, this.platform.Characteristic.PositionState.STOPPED);
}
} else {
this.service.updateCharacteristic(this.characteristic, value);
this.state = value;
if(this.service.getCharacteristic(this.characteristic).value != value){
this.service.updateCharacteristic(this.characteristic, value);
}
}
}

async updateIO(value: boolean){
this.service.updateCharacteristic(this.platform.Characteristic.On, value);
this.state = value;
if(this.service.getCharacteristic(this.platform.Characteristic.On).value != value){
this.service.updateCharacteristic(this.platform.Characteristic.On, value);
}
}
}
8 changes: 5 additions & 3 deletions src/device/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class InputHandler {
// set accessory information
this.accessory.getService(this.platform.Service.AccessoryInformation)!
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'GCE-Electronic')
.setCharacteristic(this.platform.Characteristic.Model, 'IPX-800');
.setCharacteristic(this.platform.Characteristic.Model, accessory.context.device.displayName);

switch(accessory.context.device.type) {
case 'motion': {
Expand All @@ -38,7 +38,9 @@ export class InputHandler {
this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.displayName);
}

public updateIO(state: boolean){
this.service.updateCharacteristic(this.characteristic, state);
public updateIO(value: boolean){
if(this.service.getCharacteristic(this.characteristic).value != value){
this.service.updateCharacteristic(this.characteristic, value);
}
}
}
18 changes: 15 additions & 3 deletions src/device/relay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IpxApiCaller } from '../ipx/api';
export class RelayHandler {
public readonly index: string = this.accessory.context.device.index;
private readonly service: Service;
private state;

constructor(
private readonly platform: IPXPlatform,
Expand All @@ -16,13 +17,17 @@ export class RelayHandler {
// set accessory information
this.accessory.getService(this.platform.Service.AccessoryInformation)!
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'GCE-Electronic')
.setCharacteristic(this.platform.Characteristic.Model, 'IPX-800');
.setCharacteristic(this.platform.Characteristic.Model, accessory.context.device.displayName);

switch(accessory.context.device.type) {
case 'bswitch': {
this.service = this.accessory.getService(this.platform.Service.Switch) || this.accessory.addService(this.platform.Service.Switch);
break;
}
case 'toggle': {
this.service = this.accessory.getService(this.platform.Service.Switch) || this.accessory.addService(this.platform.Service.Switch);
break;
}
case 'switch': {
this.service = this.accessory.getService(this.platform.Service.Switch) || this.accessory.addService(this.platform.Service.Switch);
break;
Expand Down Expand Up @@ -62,7 +67,14 @@ export class RelayHandler {
}
}

public updateIO(state: boolean){
this.service.updateCharacteristic(this.platform.Characteristic.On, state);
public updateIO(value: boolean){
if(this.accessory.context.device.type == 'toggle'){
this.service.updateCharacteristic(this.platform.Characteristic.On, false);
}else{
this.state = value;
if(this.service.getCharacteristic(this.platform.Characteristic.On).value != value){
this.service.updateCharacteristic(this.platform.Characteristic.On, value);
}
}
}
}
3 changes: 1 addition & 2 deletions src/ipx/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export abstract class IpxApiCaller {
//public abstract setAnaPosition(value: CharacteristicValue, platform: IPXPlatform, accessory: PlatformAccessory): void;
public abstract setVRPosition(value: CharacteristicValue, platform: IPXPlatform, accessory: PlatformAccessory): void;
public abstract setDimmerPosition(value: CharacteristicValue, platform: IPXPlatform, accessory: PlatformAccessory): void;
public abstract getStateByDeviceIndex(platform: IPXPlatform): Promise<Map<string, boolean>>;
public abstract getAnaPositionByDeviceIndex(platform: IPXPlatform): Promise<Map<string, number>>;
public abstract getState(platform: IPXPlatform);

}
Loading

0 comments on commit 5d4f767

Please sign in to comment.