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