Initial commit
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
export abstract class accessory {
|
||||||
|
public type: string
|
||||||
|
public typeId: number
|
||||||
|
|
||||||
|
constructor(type: string, typeId: number) {
|
||||||
|
this.type = type
|
||||||
|
this.typeId = typeId
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract setName(name)
|
||||||
|
abstract setValue(key, value)
|
||||||
|
abstract update()
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export interface device {
|
||||||
|
// --- Database Parameters ---
|
||||||
|
type: string
|
||||||
|
typeId: number
|
||||||
|
// --- HomeBridge Parameters ---
|
||||||
|
uniqueId: string
|
||||||
|
deviceName: string
|
||||||
|
// --- Extra Parameters ---
|
||||||
|
switchable: string
|
||||||
|
dimmable: string
|
||||||
|
detailedType: string
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'
|
||||||
|
|
||||||
|
import { accessory } from './accessory'
|
||||||
|
import { device } from './device'
|
||||||
|
import { httpRequest, response } from './httpPromise'
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
export class doorWindowSensorAccessory extends accessory {
|
||||||
|
private service: Service
|
||||||
|
|
||||||
|
private state = {
|
||||||
|
Position: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
static async discoverDevices(platform: domoticaPlatform): Promise<device[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let doorWindowSensorDevices: device[] = []
|
||||||
|
|
||||||
|
httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/doorwindowsensor')
|
||||||
|
.then((response) => {
|
||||||
|
let devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.enabled == "true") {
|
||||||
|
let object: device = {
|
||||||
|
type: "DoorWindowSensor",
|
||||||
|
typeId: device.id,
|
||||||
|
uniqueId: "DoorWindowSensor." + device.id,
|
||||||
|
deviceName: device.name,
|
||||||
|
switchable: "false",
|
||||||
|
dimmable: "false",
|
||||||
|
detailedType: device.type
|
||||||
|
}
|
||||||
|
doorWindowSensorDevices.push(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(doorWindowSensorDevices)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject('doorWindowSensorAccessory::discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly platform: domoticaPlatform,
|
||||||
|
private readonly accessory: PlatformAccessory,
|
||||||
|
) {
|
||||||
|
super("DoorWindowSensor", accessory.context.device.typeId)
|
||||||
|
this.accessory.getService(this.platform.Service.AccessoryInformation)!
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Xiaomi')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Model, 'MCCGQ01LM')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.SerialNumber, '04000' + accessory.context.device.typeId)
|
||||||
|
|
||||||
|
if (accessory.context.device.detailedType == "door") {
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Door) || this.accessory.addService(this.platform.Service.Door)
|
||||||
|
} else { // window
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Window) || this.accessory.addService(this.platform.Service.Window)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.CurrentPosition)
|
||||||
|
.onGet(this.getCurrentPosition.bind(this));
|
||||||
|
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.PositionState)
|
||||||
|
.onGet(this.getPositionState.bind(this));
|
||||||
|
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.TargetPosition)
|
||||||
|
.onGet(this.getTargetPosition.bind(this))
|
||||||
|
|
||||||
|
this.setName(accessory.context.device.deviceName)
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name) {
|
||||||
|
this.service.setCharacteristic(this.platform.Characteristic.Name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(key, value) {
|
||||||
|
if (key == "state" && value != "") {
|
||||||
|
if (value == "Off") {
|
||||||
|
this.state.Position = 0
|
||||||
|
} else {
|
||||||
|
this.state.Position = 100
|
||||||
|
}
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.CurrentPosition, this.state.Position)
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.TargetPosition, this.state.Position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const data = JSON.stringify([{
|
||||||
|
type: 'doorwindow',
|
||||||
|
query: 'state'
|
||||||
|
}])
|
||||||
|
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data)
|
||||||
|
.then((response) => {
|
||||||
|
const devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices.doorwindow) {
|
||||||
|
if (device.id == this.accessory.context.device.typeId) {
|
||||||
|
if (device.state == 'closed') {
|
||||||
|
this.state.Position = 0
|
||||||
|
} else { // open
|
||||||
|
this.state.Position = 100
|
||||||
|
}
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.CurrentPosition, this.state.Position)
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.TargetPosition, this.state.Position)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('doorWindowSensorAccessory::update Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentPosition(): CharacteristicValue {
|
||||||
|
return this.state.Position
|
||||||
|
}
|
||||||
|
|
||||||
|
getPositionState(): CharacteristicValue {
|
||||||
|
return this.platform.Characteristic.PositionState.STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTargetPosition(): CharacteristicValue {
|
||||||
|
return this.state.Position
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(): CharacteristicValue {
|
||||||
|
return this.state.Position
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'
|
||||||
|
|
||||||
|
import { accessory } from './accessory'
|
||||||
|
import { device } from './device'
|
||||||
|
import { httpRequest, response } from './httpPromise'
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
export class environmentSensorAccessory extends accessory {
|
||||||
|
private serviceTemperature: Service
|
||||||
|
private serviceHumidity: Service
|
||||||
|
|
||||||
|
private state = {
|
||||||
|
Temperature: 0,
|
||||||
|
Humidity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
static async discoverDevices(platform: domoticaPlatform): Promise<device[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let environmentSensorDevices: device[] = []
|
||||||
|
|
||||||
|
httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/environmentsensor')
|
||||||
|
.then((response) => {
|
||||||
|
let devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.enabled == "true") {
|
||||||
|
let object: device = {
|
||||||
|
type: "EnvironmentSensor",
|
||||||
|
typeId: device.id,
|
||||||
|
uniqueId: "EnvironmentSensor." + device.id,
|
||||||
|
deviceName: device.name,
|
||||||
|
switchable: "false",
|
||||||
|
dimmable: "false",
|
||||||
|
detailedType: ""
|
||||||
|
}
|
||||||
|
environmentSensorDevices.push(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(environmentSensorDevices)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject('environmentSensorAccessory::discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly platform: domoticaPlatform,
|
||||||
|
private readonly accessory: PlatformAccessory,
|
||||||
|
) {
|
||||||
|
super("EnvironmentSensor", accessory.context.device.typeId)
|
||||||
|
this.accessory.getService(this.platform.Service.AccessoryInformation)!
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Xiaomi')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Model, 'WSDCGQ01LM')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.SerialNumber, '03000' + accessory.context.device.typeId)
|
||||||
|
|
||||||
|
this.serviceTemperature = this.accessory.getService(this.platform.Service.TemperatureSensor) || this.accessory.addService(this.platform.Service.TemperatureSensor)
|
||||||
|
this.serviceTemperature.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
|
||||||
|
.onGet(this.getTemperature.bind(this))
|
||||||
|
|
||||||
|
this.serviceHumidity = this.accessory.getService(this.platform.Service.HumiditySensor) || this.accessory.addService(this.platform.Service.HumiditySensor)
|
||||||
|
this.serviceHumidity.getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity)
|
||||||
|
.onGet(this.getHumidity.bind(this))
|
||||||
|
|
||||||
|
this.setName(accessory.context.device.deviceName)
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name) {
|
||||||
|
this.serviceTemperature.setCharacteristic(this.platform.Characteristic.Name, name)
|
||||||
|
this.serviceHumidity.setCharacteristic(this.platform.Characteristic.Name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(key, value) {
|
||||||
|
if (key == "temperature" && value != "") {
|
||||||
|
this.state.Temperature = value / 100
|
||||||
|
this.serviceTemperature.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.state.Temperature)
|
||||||
|
}
|
||||||
|
if (key == "humidity" && value != "") {
|
||||||
|
this.state.Humidity = value / 100
|
||||||
|
this.serviceHumidity.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.state.Humidity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const data = JSON.stringify([{
|
||||||
|
type: 'environment',
|
||||||
|
query: 'state'
|
||||||
|
}])
|
||||||
|
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data)
|
||||||
|
.then((response) => {
|
||||||
|
const devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices.environment) {
|
||||||
|
if (device.id == this.accessory.context.device.typeId) {
|
||||||
|
this.state.Temperature = device.temperature
|
||||||
|
this.state.Humidity = device.humidity
|
||||||
|
this.serviceTemperature.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.state.Temperature)
|
||||||
|
this.serviceHumidity.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.state.Humidity)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('environmentSensorAccessory::getTemperature Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getTemperature(): CharacteristicValue {
|
||||||
|
return this.state.Temperature
|
||||||
|
}
|
||||||
|
|
||||||
|
getHumidity(): CharacteristicValue {
|
||||||
|
return this.state.Humidity
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const http = require('http')
|
||||||
|
const https = require('https')
|
||||||
|
|
||||||
|
export interface response {
|
||||||
|
statusCode: number,
|
||||||
|
headers: object,
|
||||||
|
body: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function httpRequest(urlOptions, data = ''): Promise<response> {
|
||||||
|
let operator = http
|
||||||
|
if (urlOptions.startsWith('https'))
|
||||||
|
operator = https
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const request = operator.request(urlOptions,
|
||||||
|
(result) => {
|
||||||
|
let body = ''
|
||||||
|
result.on('data', (chunk = '') => (body += chunk))
|
||||||
|
result.on('error', reject)
|
||||||
|
result.on('end', () => {
|
||||||
|
if (result.statusCode >= 200 && result.statusCode <= 299) {
|
||||||
|
resolve({statusCode: result.statusCode, headers: result.headers, body: body})
|
||||||
|
} else {
|
||||||
|
reject('Request failed. status: ' + result.statusCode + ', body: ' + body)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
request.on('error', reject)
|
||||||
|
request.write(data, 'binary')
|
||||||
|
request.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { PlatformConfig } from 'homebridge'
|
||||||
|
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
const request = require("request")
|
||||||
|
const http = require('http')
|
||||||
|
const url = require('url')
|
||||||
|
|
||||||
|
export class httpServer {
|
||||||
|
private address
|
||||||
|
private port
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly platform: domoticaPlatform,
|
||||||
|
private readonly config: PlatformConfig
|
||||||
|
) {
|
||||||
|
this.address = "0.0.0.0"
|
||||||
|
this.port = 8080
|
||||||
|
}
|
||||||
|
|
||||||
|
serverCallback (request, response) {
|
||||||
|
var urlObject = url.parse(request.url, true)
|
||||||
|
var urlQuery = urlObject.query
|
||||||
|
var body: Uint8Array[] = []
|
||||||
|
var log = this.platform.log
|
||||||
|
|
||||||
|
request.on('error', (err) => {
|
||||||
|
log.debug("[ERROR Homebridge Domotica HttpServer] Reason: %s.", err)
|
||||||
|
}).on('data', (chunk) => {
|
||||||
|
body.push(chunk)
|
||||||
|
}).on('end', () => {
|
||||||
|
var bodyString = Buffer.concat(body).toString()
|
||||||
|
|
||||||
|
response.on('error', function(err) {
|
||||||
|
log.debug("[ERROR Homebridge Domotica HttpServer] Reason: %s.", err)
|
||||||
|
})
|
||||||
|
|
||||||
|
response.statusCode = 200
|
||||||
|
response.setHeader('Content-Type', 'application/json')
|
||||||
|
|
||||||
|
if (!urlQuery.type || !urlQuery.typeId) {
|
||||||
|
response.statusCode = 404
|
||||||
|
response.setHeader("Content-Type", "text/plain")
|
||||||
|
var errorText = "[ERROR Homebridge Domotica HttpServer] Type or TypeId missing in request."
|
||||||
|
response.write(errorText)
|
||||||
|
response.end()
|
||||||
|
} else {
|
||||||
|
var type = urlQuery.type
|
||||||
|
var typeId = urlQuery.typeId
|
||||||
|
|
||||||
|
if (type != "PowerSocket" && type != "PowerSwitch" && type != "Xiaomi") {
|
||||||
|
log.debug(request.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == "Xiaomi") {
|
||||||
|
type = urlQuery.model
|
||||||
|
if (type == "TemperatureHumiditySensor") {
|
||||||
|
type = "EnvironmentSensor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var foundAccessory
|
||||||
|
|
||||||
|
for (const accessory of this.platform.accessories) {
|
||||||
|
if (accessory.type == type && accessory.typeId == typeId) {
|
||||||
|
foundAccessory = accessory
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundAccessory) {
|
||||||
|
for (const key of Object.keys(urlQuery)) {
|
||||||
|
if (key != "type" && key != "typeId") {
|
||||||
|
foundAccessory.setValue(key, urlQuery[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseBody = { "success" : true };
|
||||||
|
|
||||||
|
response.write(JSON.stringify(responseBody))
|
||||||
|
response.end()
|
||||||
|
} else {
|
||||||
|
response.setHeader("Content-Type", "text/plain")
|
||||||
|
errorText = "[ERROR Homebridge Domotica HttpServer] " + type + " " + typeId + " not found."
|
||||||
|
if (type != "PowerSocket") {
|
||||||
|
log.debug(errorText + '(' + request.url + ')')
|
||||||
|
}
|
||||||
|
response.write(errorText)
|
||||||
|
response.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
http.createServer(this.serverCallback.bind(this)).listen(this.port, this.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { API } from 'homebridge'
|
||||||
|
|
||||||
|
import { PLATFORM_NAME } from './settings'
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
export = (api: API) => {
|
||||||
|
api.registerPlatform(PLATFORM_NAME, domoticaPlatform)
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'
|
||||||
|
|
||||||
|
import { accessory } from './accessory'
|
||||||
|
import { device } from './device'
|
||||||
|
import { httpRequest, response } from './httpPromise'
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
export class klikAanKlikUitAccessory extends accessory {
|
||||||
|
private service: Service
|
||||||
|
|
||||||
|
private state = {
|
||||||
|
On: false,
|
||||||
|
Brightness: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
static async discoverDevices(platform: domoticaPlatform): Promise<device[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let klikaanklikuitDevices: device[] = []
|
||||||
|
|
||||||
|
httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/klikaanklikuit')
|
||||||
|
.then((response) => {
|
||||||
|
let devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.enabled == "true") {
|
||||||
|
let object: device = {
|
||||||
|
type: "KlikAanKlikUit",
|
||||||
|
typeId: device.id,
|
||||||
|
uniqueId: "KlikAanKlikUit." + device.id,
|
||||||
|
deviceName: device.name,
|
||||||
|
switchable: "true",
|
||||||
|
dimmable: device.dimmable,
|
||||||
|
detailedType: ""
|
||||||
|
}
|
||||||
|
klikaanklikuitDevices.push(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(klikaanklikuitDevices)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject('klikAanKlikUitAccessory::discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly platform: domoticaPlatform,
|
||||||
|
private readonly accessory: PlatformAccessory,
|
||||||
|
) {
|
||||||
|
super("KlikAanKlikUit", accessory.context.device.typeId)
|
||||||
|
this.accessory.getService(this.platform.Service.AccessoryInformation)!
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'KlikAanKlikUit')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Model, 'AC-1000')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.SerialNumber, '01000' + accessory.context.device.typeId)
|
||||||
|
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Lightbulb) || this.accessory.addService(this.platform.Service.Lightbulb)
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.On)
|
||||||
|
.onSet(this.setOn.bind(this))
|
||||||
|
.onGet(this.getOn.bind(this))
|
||||||
|
|
||||||
|
if (accessory.context.device.dimmable != "false") { // WTF?
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
|
||||||
|
.onSet(this.setBrightness.bind(this))
|
||||||
|
.onGet(this.getBrightness.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setName(accessory.context.device.deviceName)
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setName(name) {
|
||||||
|
this.service.setCharacteristic(this.platform.Characteristic.Name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(key, value) {
|
||||||
|
if (key == "state" && value != "") {
|
||||||
|
this.state.On = (value == "on")
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.state.On)
|
||||||
|
} else if (key == "dimLevel" && value!= "") {
|
||||||
|
this.state.Brightness = value
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.state.Brightness)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const data = JSON.stringify([{
|
||||||
|
type: 'klikaanklikuit',
|
||||||
|
query: 'state'
|
||||||
|
}])
|
||||||
|
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data)
|
||||||
|
.then((response) => {
|
||||||
|
const devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices.klikaanklikuit) {
|
||||||
|
if (device.id == this.accessory.context.device.typeId) {
|
||||||
|
if (device.state == 'on') {
|
||||||
|
this.state.On = true
|
||||||
|
} else {
|
||||||
|
this.state.On = false
|
||||||
|
}
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.state.On)
|
||||||
|
|
||||||
|
if (device.dimmable != 'false') { // WTF?
|
||||||
|
this.state.Brightness = device.dimLevel;
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.state.Brightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('klikAanKlikUitAccessory::update Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async setOn(value: CharacteristicValue) {
|
||||||
|
this.state.On = value as boolean
|
||||||
|
|
||||||
|
// http://$domoticaIP/external/WebUpdate.php?data=KlikAanKlikUit/$id/$state/
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + '/external/WebUpdate.php?data=KlikAanKlikUit/' + this.accessory.context.device.typeId + '/' + (this.state.On? '1': '0') + '/')
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('klikAanKlikUitAccessory::setOn Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getOn(): CharacteristicValue {
|
||||||
|
return this.state.On
|
||||||
|
}
|
||||||
|
|
||||||
|
async setBrightness(value: CharacteristicValue) {
|
||||||
|
this.state.Brightness = value as number
|
||||||
|
|
||||||
|
// http://$domoticaIP/external/WebUpdate.php?data=KlikAanKlikUit/$id/$state/$dimLevel
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + '/external/WebUpdate.php?data=KlikAanKlikUit/' + this.accessory.context.device.typeId + '//' + this.state.Brightness)
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('klikAanKlikUitAccessory::setBrightness Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getBrightness(): CharacteristicValue {
|
||||||
|
return this.state.Brightness
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'
|
||||||
|
|
||||||
|
import { accessory } from './accessory'
|
||||||
|
import { device } from './device'
|
||||||
|
import { httpRequest, response } from './httpPromise'
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
export class motionSensorAccessory extends accessory {
|
||||||
|
private service: Service
|
||||||
|
|
||||||
|
private state = {
|
||||||
|
Motion: false
|
||||||
|
}
|
||||||
|
|
||||||
|
static async discoverDevices(platform: domoticaPlatform): Promise<device[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let motionSensorDevices: device[] = []
|
||||||
|
|
||||||
|
httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/motionsensor')
|
||||||
|
.then((response) => {
|
||||||
|
let devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.enabled == "true") {
|
||||||
|
let object: device = {
|
||||||
|
type: "MotionSensor",
|
||||||
|
typeId: device.id,
|
||||||
|
uniqueId: "MotionSensor." + device.id,
|
||||||
|
deviceName: device.name,
|
||||||
|
switchable: "false",
|
||||||
|
dimmable: "false",
|
||||||
|
detailedType: device.type
|
||||||
|
}
|
||||||
|
motionSensorDevices.push(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(motionSensorDevices)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject('motionSensorAccessory::discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly platform: domoticaPlatform,
|
||||||
|
private readonly accessory: PlatformAccessory,
|
||||||
|
) {
|
||||||
|
super("MotionSensor", accessory.context.device.typeId)
|
||||||
|
this.accessory.getService(this.platform.Service.AccessoryInformation)!
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Xiaomi')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Model, 'RTCGQ01LM')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.SerialNumber, '05000' + accessory.context.device.typeId)
|
||||||
|
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.MotionSensor) || this.accessory.addService(this.platform.Service.MotionSensor)
|
||||||
|
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.MotionDetected)
|
||||||
|
.onGet(this.getState.bind(this));
|
||||||
|
|
||||||
|
this.setName(accessory.context.device.deviceName)
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name) {
|
||||||
|
this.service.setCharacteristic(this.platform.Characteristic.Name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(key, value) {
|
||||||
|
if (key == "state" && value != "") {
|
||||||
|
if (value == "On") {
|
||||||
|
this.state.Motion = true
|
||||||
|
} else {
|
||||||
|
this.state.Motion = false
|
||||||
|
}
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.state.Motion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const data = JSON.stringify([{
|
||||||
|
type: 'motion',
|
||||||
|
query: 'state'
|
||||||
|
}])
|
||||||
|
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data)
|
||||||
|
.then((response) => {
|
||||||
|
const devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices.motion) {
|
||||||
|
if (device.id == this.accessory.context.device.typeId) {
|
||||||
|
if (device.state == 'absent') {
|
||||||
|
this.state.Motion = false
|
||||||
|
} else { // present
|
||||||
|
this.state.Motion = true
|
||||||
|
}
|
||||||
|
return this.state.Motion
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.state.Motion)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('motionSensorAccessory::update Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getState(): CharacteristicValue {
|
||||||
|
return this.state.Motion
|
||||||
|
}
|
||||||
|
}
|
||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge'
|
||||||
|
|
||||||
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings'
|
||||||
|
|
||||||
|
import { accessory } from './accessory'
|
||||||
|
import { device } from './device'
|
||||||
|
import { klikAanKlikUitAccessory } from './klikAanKlikUitAccessory'
|
||||||
|
import { powerSocketAccessory } from './powerSocketAccessory'
|
||||||
|
import { powerSwitchAccessory } from './powerSwitchAccessory'
|
||||||
|
import { environmentSensorAccessory } from './environmentSensorAccessory'
|
||||||
|
import { doorWindowSensorAccessory } from './doorWindowSensorAccessory'
|
||||||
|
import { motionSensorAccessory } from './motionSensorAccessory'
|
||||||
|
import { httpServer } from './httpServer'
|
||||||
|
|
||||||
|
export class domoticaPlatform implements DynamicPlatformPlugin {
|
||||||
|
public readonly Service: typeof Service = this.api.hap.Service
|
||||||
|
public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic
|
||||||
|
|
||||||
|
public readonly accessories: accessory[] = []
|
||||||
|
public readonly cachedAccessories: PlatformAccessory[] = []
|
||||||
|
public readonly presentAccessories: PlatformAccessory[] = []
|
||||||
|
|
||||||
|
private readonly discoveries: String[] = []
|
||||||
|
private readonly completedDiscoveries: String[] = []
|
||||||
|
|
||||||
|
private httpServer: httpServer = new httpServer(this, this.config)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly log: Logger,
|
||||||
|
public readonly config: PlatformConfig,
|
||||||
|
public readonly api: API
|
||||||
|
) {
|
||||||
|
this.api.on('didFinishLaunching', () => {
|
||||||
|
this.discoverDevices()
|
||||||
|
this.httpServer.start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
configureAccessory(accessory: PlatformAccessory) {
|
||||||
|
this.cachedAccessories.push(accessory)
|
||||||
|
}
|
||||||
|
|
||||||
|
discoverDevices() {
|
||||||
|
this.discoveryStarted('klikAanKlikUitAccessory')
|
||||||
|
klikAanKlikUitAccessory.discoverDevices(this)
|
||||||
|
.then((devices) => {
|
||||||
|
this.processDiscoveredDevices(devices)
|
||||||
|
this.discoveryCompleted('klikAanKlikUitAccessory')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.log.debug('klikAanKlikUitAccessory.discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.discoveryStarted('powerSocketAccessory')
|
||||||
|
powerSocketAccessory.discoverDevices(this)
|
||||||
|
.then((devices) => {
|
||||||
|
this.processDiscoveredDevices(devices)
|
||||||
|
this.discoveryCompleted('powerSocketAccessory')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.log.debug('powerSocketAccessory.discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.discoveryStarted('powerSwitchAccessory')
|
||||||
|
powerSwitchAccessory.discoverDevices(this)
|
||||||
|
.then((devices) => {
|
||||||
|
this.processDiscoveredDevices(devices)
|
||||||
|
this.discoveryCompleted('powerSwitchAccessory')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.log.debug('powerSwitchAccessory.discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.discoveryStarted('environmentSensorAccessory')
|
||||||
|
environmentSensorAccessory.discoverDevices(this)
|
||||||
|
.then((devices) => {
|
||||||
|
this.processDiscoveredDevices(devices)
|
||||||
|
this.discoveryCompleted('environmentSensorAccessory')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.log.debug('environmentSensorAccessory.discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.discoveryStarted('doorWindowSensorAccessory')
|
||||||
|
doorWindowSensorAccessory.discoverDevices(this)
|
||||||
|
.then((devices) => {
|
||||||
|
this.processDiscoveredDevices(devices)
|
||||||
|
this.discoveryCompleted('doorWindowSensorAccessory')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.log.debug('doorWindowSensorAccessory.discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.discoveryStarted('motionSensorAccessory')
|
||||||
|
motionSensorAccessory.discoverDevices(this)
|
||||||
|
.then((devices) => {
|
||||||
|
this.processDiscoveredDevices(devices)
|
||||||
|
this.discoveryCompleted('motionSensorAccessory')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.log.debug('motionSensorAccessory.discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
processDiscoveredDevices(domoticaDevices: device[]) {
|
||||||
|
for (const device of domoticaDevices) {
|
||||||
|
const uuid = this.api.hap.uuid.generate(device.uniqueId)
|
||||||
|
const existingAccessory = this.cachedAccessories.find(accessory => accessory.UUID === uuid)
|
||||||
|
|
||||||
|
if (existingAccessory) {
|
||||||
|
this.log.info('| ' + existingAccessory.context.device.type + ': ' + existingAccessory.displayName)
|
||||||
|
|
||||||
|
this.presentAccessories.push(existingAccessory)
|
||||||
|
|
||||||
|
if (existingAccessory.displayName != device.deviceName) {
|
||||||
|
this.log.info('! ' + existingAccessory.displayName + ' => ' + device.deviceName)
|
||||||
|
existingAccessory.context.device = device
|
||||||
|
this.api.updatePlatformAccessories([existingAccessory])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingAccessory.context.device.type == 'KlikAanKlikUit') {
|
||||||
|
this.accessories.push(new klikAanKlikUitAccessory(this, existingAccessory))
|
||||||
|
} else if (existingAccessory.context.device.type == 'PowerSocket') {
|
||||||
|
this.accessories.push(new powerSocketAccessory(this, existingAccessory))
|
||||||
|
} else if (existingAccessory.context.device.type == 'PowerSwitch') {
|
||||||
|
this.accessories.push(new powerSwitchAccessory(this, existingAccessory))
|
||||||
|
} else if (existingAccessory.context.device.type == 'EnvironmentSensor') {
|
||||||
|
this.accessories.push(new environmentSensorAccessory(this, existingAccessory))
|
||||||
|
} else if (existingAccessory.context.device.type == 'DoorWindowSensor') {
|
||||||
|
this.accessories.push(new doorWindowSensorAccessory(this, existingAccessory))
|
||||||
|
} else if (existingAccessory.context.device.type == 'MotionSensor') {
|
||||||
|
this.accessories.push(new motionSensorAccessory(this, existingAccessory))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.log.info('+ ' + device.type + ': ' + device.deviceName)
|
||||||
|
|
||||||
|
const accessory = new this.api.platformAccessory(device.deviceName, uuid)
|
||||||
|
accessory.context.device = device
|
||||||
|
|
||||||
|
if (accessory.context.device.type == 'KlikAanKlikUit') {
|
||||||
|
this.accessories.push(new klikAanKlikUitAccessory(this, accessory))
|
||||||
|
} else if (accessory.context.device.type == 'PowerSocket') {
|
||||||
|
this.accessories.push(new powerSocketAccessory(this, accessory))
|
||||||
|
} else if (accessory.context.device.type == 'PowerSwitch') {
|
||||||
|
this.accessories.push(new powerSwitchAccessory(this, accessory))
|
||||||
|
} else if (accessory.context.device.type == 'EnvironmentSensor') {
|
||||||
|
this.accessories.push(new environmentSensorAccessory(this, accessory))
|
||||||
|
} else if (accessory.context.device.type == 'DoorWindowSensor') {
|
||||||
|
this.accessories.push(new doorWindowSensorAccessory(this, accessory))
|
||||||
|
} else if (accessory.context.device.type == 'MotionSensor') {
|
||||||
|
this.accessories.push(new motionSensorAccessory(this, accessory))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
discoveryStarted(deviceType) {
|
||||||
|
this.discoveries.push(deviceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
discoveryCompleted(deviceType) {
|
||||||
|
this.completedDiscoveries.push(deviceType)
|
||||||
|
|
||||||
|
if (this.completedDiscoveries.length == this.discoveries.length) {
|
||||||
|
this.cleanupRemovedDevices()
|
||||||
|
}
|
||||||
|
|
||||||
|
//setInterval(this.updateAccessories.bind(this), 60 * 1000); // Poll every minute
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAccessories() {
|
||||||
|
for (const accessory of this.accessories) {
|
||||||
|
accessory.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupRemovedDevices() {
|
||||||
|
for (const existingAccessory of this.cachedAccessories) {
|
||||||
|
const presentAccessory = this.presentAccessories.find(accessory => accessory.UUID === existingAccessory.UUID)
|
||||||
|
|
||||||
|
if (!presentAccessory) {
|
||||||
|
this.log.info('- ' + existingAccessory.context.device.type + ': ' + existingAccessory.displayName)
|
||||||
|
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 accessory: PlatformAccessory,
|
||||||
|
) {
|
||||||
|
|
||||||
|
// 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 sub type 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
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 possbile. 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'
|
||||||
|
|
||||||
|
import { accessory } from './accessory'
|
||||||
|
import { device } from './device'
|
||||||
|
import { httpRequest, response } from './httpPromise'
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
export class powerSocketAccessory extends accessory {
|
||||||
|
private service: Service
|
||||||
|
|
||||||
|
private state = {
|
||||||
|
On: false
|
||||||
|
}
|
||||||
|
|
||||||
|
static async discoverDevices(platform: domoticaPlatform): Promise<device[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let powersocketDevices: device[] = []
|
||||||
|
|
||||||
|
httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/powersocket')
|
||||||
|
.then((response) => {
|
||||||
|
let devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.enabled == "true" &&
|
||||||
|
device.switchable == "true") {
|
||||||
|
let object: device = {
|
||||||
|
type: "PowerSocket",
|
||||||
|
typeId: device.id,
|
||||||
|
uniqueId: "PowerSocket." + device.id,
|
||||||
|
deviceName: device.name,
|
||||||
|
switchable: device.switchable,
|
||||||
|
dimmable: "false",
|
||||||
|
detailedType: device.type
|
||||||
|
}
|
||||||
|
powersocketDevices.push(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(powersocketDevices)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject('powerSocketAccessory::discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly platform: domoticaPlatform,
|
||||||
|
private readonly accessory: PlatformAccessory,
|
||||||
|
) {
|
||||||
|
super("PowerSocket", accessory.context.device.typeId)
|
||||||
|
this.accessory.getService(this.platform.Service.AccessoryInformation)!
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'PowerSocket')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Model, 'BW-SHP6')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.SerialNumber, '02000' + accessory.context.device.typeId)
|
||||||
|
|
||||||
|
if (accessory.context.device.detailedType == "light") {
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Lightbulb) || this.accessory.addService(this.platform.Service.Lightbulb)
|
||||||
|
} else { // powersocket
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Outlet) || this.accessory.addService(this.platform.Service.Outlet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessory.context.device.switchable == "true") {
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.On)
|
||||||
|
.onSet(this.setOn.bind(this))
|
||||||
|
.onGet(this.getOn.bind(this))
|
||||||
|
} else {
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.On)
|
||||||
|
.onGet(this.getOn.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setName(accessory.context.device.deviceName)
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name) {
|
||||||
|
this.service.setCharacteristic(this.platform.Characteristic.Name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(key, value) {
|
||||||
|
if (key == "state" && value != "") {
|
||||||
|
this.state.On = (value == "1")
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.state.On)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const data = JSON.stringify([{
|
||||||
|
type: 'powersocket',
|
||||||
|
query: 'state'
|
||||||
|
}])
|
||||||
|
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data)
|
||||||
|
.then((response) => {
|
||||||
|
const devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices.powersocket) {
|
||||||
|
if (device.id == this.accessory.context.device.typeId) {
|
||||||
|
if (device.state == 'on') {
|
||||||
|
this.state.On = true
|
||||||
|
} else {
|
||||||
|
this.state.On = false
|
||||||
|
}
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.state.On)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('powerSocketAccessory::update Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async setOn(value: CharacteristicValue) {
|
||||||
|
this.state.On = value as boolean
|
||||||
|
|
||||||
|
// http://$domoticaIP/external/WebUpdate.php?data=PowerSocket/$id/$state/
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + '/external/WebUpdate.php?data=PowerSocket/' + this.accessory.context.device.typeId + '/' + (this.state.On? '1': '0') + '/')
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('powerSocketAccessory::setOn Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getOn(): CharacteristicValue {
|
||||||
|
return this.state.On
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'
|
||||||
|
|
||||||
|
import { accessory } from './accessory'
|
||||||
|
import { device } from './device'
|
||||||
|
import { httpRequest, response } from './httpPromise'
|
||||||
|
import { domoticaPlatform } from './platform'
|
||||||
|
|
||||||
|
export class powerSwitchAccessory extends accessory {
|
||||||
|
private service: Service
|
||||||
|
|
||||||
|
private state = {
|
||||||
|
On: false,
|
||||||
|
Brightness: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
static async discoverDevices(platform: domoticaPlatform): Promise<device[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let powerswitchDevices: device[] = []
|
||||||
|
|
||||||
|
httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/powerswitch')
|
||||||
|
.then((response) => {
|
||||||
|
let devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.enabled == "true") {
|
||||||
|
let object: device = {
|
||||||
|
type: "PowerSwitch",
|
||||||
|
typeId: device.id,
|
||||||
|
uniqueId: "PowerSwitch." + device.id,
|
||||||
|
deviceName: device.name,
|
||||||
|
switchable: "true",
|
||||||
|
dimmable: device.dimmable,
|
||||||
|
detailedType: device.type
|
||||||
|
}
|
||||||
|
powerswitchDevices.push(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(powerswitchDevices)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject('powerSwitchAccessory::discoverDevices Error ->' + error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly platform: domoticaPlatform,
|
||||||
|
private readonly accessory: PlatformAccessory,
|
||||||
|
) {
|
||||||
|
super("PowerSwitch", accessory.context.device.typeId)
|
||||||
|
this.accessory.getService(this.platform.Service.AccessoryInformation)!
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'PowerSwitch')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.Model, 'Shelly')
|
||||||
|
.setCharacteristic(this.platform.Characteristic.SerialNumber, '06000' + accessory.context.device.typeId)
|
||||||
|
|
||||||
|
if (accessory.context.device.detailedType == "light") {
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Lightbulb) || this.accessory.addService(this.platform.Service.Lightbulb)
|
||||||
|
} else if (accessory.context.device.detailedType == "fan") {
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Fan) || this.accessory.addService(this.platform.Service.Fan)
|
||||||
|
} else { // other
|
||||||
|
this.service = this.accessory.getService(this.platform.Service.Outlet) || this.accessory.addService(this.platform.Service.Outlet)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.On)
|
||||||
|
.onSet(this.setOn.bind(this))
|
||||||
|
.onGet(this.getOn.bind(this))
|
||||||
|
|
||||||
|
if (accessory.context.device.dimmable != "false") { // WTF?
|
||||||
|
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
|
||||||
|
.onSet(this.setBrightness.bind(this))
|
||||||
|
.onGet(this.getBrightness.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setName(accessory.context.device.deviceName)
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name) {
|
||||||
|
this.service.setCharacteristic(this.platform.Characteristic.Name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(key, value) {
|
||||||
|
if (key == "state" && value != "") {
|
||||||
|
this.state.On = (value == "1")
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.state.On)
|
||||||
|
} else if (key == "dimLevel" && value != "") {
|
||||||
|
this.state.Brightness = value
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.state.Brightness)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const data = JSON.stringify([{
|
||||||
|
type: 'powerswitch',
|
||||||
|
query: 'state'
|
||||||
|
}])
|
||||||
|
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data)
|
||||||
|
.then((response) => {
|
||||||
|
const devices = JSON.parse(response.body)
|
||||||
|
|
||||||
|
for (const device of devices.powerswitch) {
|
||||||
|
if (device.id == this.accessory.context.device.typeId) {
|
||||||
|
if (device.state == 'on') {
|
||||||
|
this.state.On = true
|
||||||
|
} else {
|
||||||
|
this.state.On = false
|
||||||
|
}
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.On, this.state.On)
|
||||||
|
|
||||||
|
if (device.dimmable != 'false') { // WTF?
|
||||||
|
this.state.Brightness = device.dimlevel;
|
||||||
|
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.state.Brightness)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('powerSwitchAccessory::update Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async setOn(value: CharacteristicValue) {
|
||||||
|
this.state.On = value as boolean
|
||||||
|
|
||||||
|
// http://$domoticaIP/external/WebUpdate.php?data=PowerSwitch/$id/$state/
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + '/external/WebUpdate.php?data=PowerSwitch/' + this.accessory.context.device.typeId + '/' + (this.state.On? '1': '0') + '/')
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('powerSwitchAccessory::setOn Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getOn(): CharacteristicValue {
|
||||||
|
return this.state.On
|
||||||
|
}
|
||||||
|
|
||||||
|
async setBrightness(value: CharacteristicValue) {
|
||||||
|
this.state.Brightness = value as number
|
||||||
|
|
||||||
|
// http://$domoticaIP/external/WebUpdate.php?data=PowerSwitch/$id/$state/$dimLevel
|
||||||
|
httpRequest("http://" + this.platform.config.hostname + '/external/WebUpdate.php?data=PowerSwitch/' + this.accessory.context.device.typeId + '//' + this.state.Brightness)
|
||||||
|
.catch((error) => {
|
||||||
|
this.platform.log.debug('powerSwitchAccessory::setBrightness Error ->' + error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getBrightness(): CharacteristicValue {
|
||||||
|
return this.state.Brightness
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const PLATFORM_NAME = 'Domotica'
|
||||||
|
export const PLUGIN_NAME = 'homebridge-domotica'
|
||||||
Reference in New Issue
Block a user