Closed mda30 closed 7 years ago
i found that it seems to be prone on doing it when its in realtime mode and it has a timeout, so you could catch the error there, i also have an NAN return sometimes, so that also needs to be cought, ive started building a persistant store for modules with an slow rolling poll in the background, so that it will update in the background, unless when the IOS connects it will force a quick poll of all devices in 60 seconds and update the display, either way it would serve from the store, eliminating the lag when loading in "yes " mode, but im new to js, so it may take a while, i have the persistant store running already thou
Happy to help test when you have something ready.
It's slow going :)
Regards
Jaco Goussard
On 22 Sep 2016, at 9:47 AM, mda30 notifications@github.com wrote:
Happy to help test when you have something ready.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.
so ive gotten this far, used some modules from homebridge-people and learned from that code.
I need to figure out to create a branch, then i will do better, im just hacking away really, i am using the "yes" feature to pull the requests, but have made so that it doesn't make so many requests, and only pulls updates after 5 mins, its hardcoded at the moment, but i will get better at this as well.
basically after you refreshed once, you can let the phone time out or what ever, the next pull you do will be close on instant.
let me know what you think @mda30
var Service, Characteristic, HomebridgeAPI;
var request = require("request");
var pollingtoevent = require('polling-to-event');
var moment = require('moment');
module.exports = function(homebridge){
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
HomebridgeAPI = homebridge;
homebridge.registerAccessory("homebridge-http", "Http", HttpAccessory);
}
function HttpAccessory(log, config) {
this.log = log;
// url info
this.on_url = config["on_url"];
this.on_body = config["on_body"];
this.off_url = config["off_url"];
this.off_body = config["off_body"];
this.status_url = config["status_url"];
this.brightness_url = config["brightness_url"];
this.brightnesslvl_url = config["brightnesslvl_url"];
this.http_method = config["http_method"] || "GET";;
this.http_brightness_method = config["http_brightness_method"] || this.http_method;
this.username = config["username"] || "";
this.password = config["password"] || "";
this.sendimmediately = config["sendimmediately"] || "";
this.service = config["service"] || "Switch";
this.name = config["name"];
this.brightnessHandling = config["brightnessHandling"] || "no";
this.switchHandling = config["switchHandling"] || "no";
//jaco
this.storage = require('node-persist');
//Init storage
this.storage.initSync({
dir: HomebridgeAPI.user.persistPath()
});
this.state = false;
this.currentlevel = 0;
var that = this;
// Status Polling, if you want to add additional services that don't use switch handling you can add something like this || (this.service=="Smoke" || this.service=="Motion"))
if (this.status_url && this.switchHandling =="realtime") {
var powerurl = this.status_url;
var statusemitter = pollingtoevent(function(done) {
that.httpRequest(powerurl, "", "GET", that.username, that.password, that.sendimmediately, function(error, response, body) {
if (error) {
that.log('HTTP get power function failed: %s', error.message);
callback(error);
} else {
done(null, body);
}
})
}, {longpolling:true,interval:80000,longpollEventName:"statuspoll"});
statusemitter.on("statuspoll", function(data) {
var binaryState = parseInt(data);
that.log(binaryState);
that.state = binaryState > 0;
that.log(that.service, "received power",that.status_url, "state is currently", binaryState);
that.storage.setItem('http_' + this.name, Date.now());
// switch used to easily add additonal services
// switch (that.service) {
// case "Switch":
// if (that.switchService ) {
// that.switchService .getCharacteristic(Characteristic.On)
// .setValue(that.state);
// }
// break;
// case "Light":
// if (that.lightbulbService) {
// that.lightbulbService.getCharacteristic(Characteristic.On)
// .setValue(that.state);
// }
// break;
// }
});
}
// Brightness Polling
if (this.brightnesslvl_url && this.brightnessHandling =="realtime") {
var brightnessurl = this.brightnesslvl_url;
var levelemitter = pollingtoevent(function(done) {
that.httpRequest(brightnessurl, "", "GET", that.username, that.password, that.sendimmediately, function(error, response, responseBody) {
if (error) {
that.log('HTTP get power function failed: %s', error.message);
return;
} else {
done(null, responseBody);
}
}) // set longer polling as slider takes longer to set value
}, {longpolling:true,interval:2000,longpollEventName:"levelpoll"});
levelemitter.on("levelpoll", function(data) {
that.currentlevel = parseInt(data);
if (that.lightbulbService) {
that.log(that.service, "received brightness",that.brightnesslvl_url, "level is currently", that.currentlevel);
that.lightbulbService.getCharacteristic(Characteristic.Brightness)
.setValue(that.currentlevel);
}
});
}
}
HttpAccessory.prototype = {
httpRequest: function(url, body, method, username, password, sendimmediately, callback) {
request({
url: url,
body: body,
method: method,
rejectUnauthorized: false,
auth: {
user: username,
pass: password,
sendImmediately: sendimmediately
}
},
function(error, response, body) {
callback(error, response, body)
})
},
setPowerState: function(powerOn, callback) {
var url;
var body;
if (!this.on_url || !this.off_url) {
this.log.warn("Ignoring request; No power url defined.");
callback(new Error("No power url defined."));
return;
}
if (powerOn) {
url = this.on_url;
body = this.on_body;
this.log("Setting power state to on");
// update status
this.storage.setItem('http_' + this.name,1);
this.storage.setItem('http_lastrefresh'+this.name, Date.now());
} else {
url = this.off_url;
body = this.off_body;
this.log("Setting power state to off");
// update status
this.storage.setItem('http_' + this.name,0);
this.storage.setItem('http_lastrefresh'+this.name, Date.now());
}
this.httpRequest(url, body, this.http_method, this.username, this.password, this.sendimmediately, function(error, response, responseBody) {
if (error) {
this.log('HTTP set power function failed: %s', error.message);
callback(error);
} else {
this.log('HTTP set power function succeeded!');
callback();
}
}.bind(this));
},
getPowerState: function(callback) {
var lastSeenUnix = this.storage.getItem('http_lastrefresh'+this.name);
var lastSeenMoment = moment(lastSeenUnix);
//var activeThreshold = moment().subtract(this.threshold, 'm');
var activeThreshold = moment().subtract(5, 'm');
var isActive = lastSeenMoment.isAfter(activeThreshold);
if (isActive)
{binaryState = parseInt(this.storage.getItem('http_' + this.name));
this.log("Recently refreshed %s", binaryState)
powerOn = binaryState > 0;
callback(null, powerOn);}
else
{
this.log("lets scan");
if (!this.status_url) {
this.log.warn("Ignoring request; No status url defined.");
callback(new Error("No status url defined."));
return;
}
var url = this.status_url;
this.log("Getting power state");
this.httpRequest(url, "", "GET", this.username, this.password, this.sendimmediately, function(error, response, responseBody) {
if (error) {
this.log('HTTP get power function failed: %s', error.message);
callback(error);
} else {
var binaryState = parseInt(responseBody);
var powerOn = binaryState > 0;
this.log("Power state is currently %s", binaryState);
if (isNaN(parseInt(responseBody))) {this.log("Exception Power state is currently %s", binaryState);
binaryState = parseInt(this.storage.getItem('http_' + this.name));
this.log("Old Power state is currently %s", binaryState)
powerOn = binaryState > 0;
callback(null, powerOn);
} else {
this.storage.setItem('http_' + this.name, parseInt(responseBody));
this.storage.setItem('http_lastrefresh'+this.name, Date.now());
callback(null, powerOn);
}
}
}.bind(this));
}
},
getBrightness: function(callback) {
if (!this.brightnesslvl_url) {
this.log.warn("Ignoring request; No brightness level url defined.");
callback(new Error("No brightness level url defined."));
return;
}
var url = this.brightnesslvl_url;
this.log("Getting Brightness level");
this.httpRequest(url, "", "GET", this.username, this.password, this.sendimmediately, function(error, response, responseBody) {
if (error) {
this.log('HTTP get brightness function failed: %s', error.message);
callback(error);
} else {
var binaryState = parseInt(responseBody);
var level = binaryState;
this.log("brightness state is currently %s", binaryState);
callback(null, level);
}
}.bind(this));
},
setBrightness: function(level, callback) {
if (!this.brightness_url) {
this.log.warn("Ignoring request; No brightness url defined.");
callback(new Error("No brightness url defined."));
return;
}
var url = this.brightness_url.replace("%b", level)
this.log("Setting brightness to %s", level);
this.httpRequest(url, "", this.http_brightness_method, this.username, this.password, this.sendimmediately, function(error, response, body) {
if (error) {
this.log('HTTP brightness function failed: %s', error);
callback(error);
} else {
this.log('HTTP brightness function succeeded!');
callback();
}
}.bind(this));
},
identify: function(callback) {
this.log("Identify requested!");
callback(); // success
},
getServices: function() {
var that = this;
// you can OPTIONALLY create an information service if you wish to override
// the default values for things like serial number, model, etc.
var informationService = new Service.AccessoryInformation();
informationService
.setCharacteristic(Characteristic.Manufacturer, "HTTP Manufacturer")
.setCharacteristic(Characteristic.Model, "HTTP Model")
.setCharacteristic(Characteristic.SerialNumber, "HTTP Serial Number");
switch (this.service) {
case "Switch":
this.switchService = new Service.Switch(this.name);
switch (this.switchHandling) {
//Power Polling
case "cached":
this.switchService
.getCharacteristic(Characteristic.On)
.on('set', this.setPowerState.bind(this));
break;
case "yes":
this.switchService
.getCharacteristic(Characteristic.On)
.on('get', this.getPowerState.bind(this))
.on('set', this.setPowerState.bind(this));
break;
case "realtime":
this.switchService
.getCharacteristic(Characteristic.On)
.on('get', function(callback) {callback(null, that.state)})
.on('set', this.setPowerState.bind(this));
break;
default :
this.switchService
.getCharacteristic(Characteristic.On)
.on('set', this.setPowerState.bind(this));
break;}
return [this.switchService];
case "Light":
this.lightbulbService = new Service.Lightbulb(this.name);
switch (this.switchHandling) {
//Power Polling
case "cache" :
this.lightbulbService
.getCharacteristic(Characteristic.On)
.on('get', this.getPowerState.bind(this))
.on('set', this.setPowerState.bind(this));
break;
case "yes" :
this.lightbulbService
.getCharacteristic(Characteristic.On)
.on('get', this.getPowerState.bind(this))
.on('set', this.setPowerState.bind(this));
break;
case "realtime":
this.lightbulbService
.getCharacteristic(Characteristic.On)
.on('get', function(callback) {callback(null, that.state)})
.on('set', this.setPowerState.bind(this));
break;
default:
this.lightbulbService
.getCharacteristic(Characteristic.On)
.on('set', this.setPowerState.bind(this));
break;
}
// Brightness Polling
if (this.brightnessHandling == "realtime") {
this.lightbulbService
.addCharacteristic(new Characteristic.Brightness())
.on('get', function(callback) {callback(null, that.currentlevel)})
.on('set', this.setBrightness.bind(this));
} else if (this.brightnessHandling == "yes") {
this.lightbulbService
.addCharacteristic(new Characteristic.Brightness())
.on('get', this.getBrightness.bind(this))
.on('set', this.setBrightness.bind(this));
}
return [informationService, this.lightbulbService];
break;
}
}
};
Just fork this repo, once you get it all working do a pull request I will look into it and see if it can be merged
ok, ive done so, still have some stuff to neaten and apply, but it's currently working for me the update is there https://github.com/cybertza/homebridge-http
@cybertza i am testing your fork now. Should i expect status to be up to date if I use "yes" and the device state is changed manually, or do I need realtime for that (in which case I will need to wait until you port the caching into 'realtime' to test most of my devices). Thanks.
EDIT:
When i launch homebridge with this installed i get
[9/24/2016, 4:43:56 PM] ERROR LOADING PLUGIN homebridge-http:
[9/24/2016, 4:43:56 PM] Error: Cannot find module 'moment'
at Function.Module._resolveFilename (module.js:455:15)
at Function.Module._load (module.js:403:25)
at Module.require (module.js:483:17)
at require (internal/module.js:20:19)
at Object.
As per the readme, It will read the status on request unless itbeen less than 5 mins, then it will read from the buffer, its not the most effective honestly, it will most likely be best on 30 seconds or something, i am busy writing cahced as the moment, but had some reading to do before, so i need to still read some,
I basically want to always read from buffer, and then after the buffer has been served action a poll of all the devices, and update the devices, then in combination have a service in the back ground that always polls the devices so that the buffer is mostly up to date.
Still thinking about the best way to do it.
For my use the current Yes works well, i really don't have that much changes in my lights that the "yes" doesn’t work on a 5 min buffer, either way, i have updated my real time code already and no longer crash on it, but it was 2 am so i'm not sure how elegant the fix is, just know it doesn’t crash anymore, that’s why i had to read more, as that’s where i needed to understand the way to address the device status better and so forth.
I will put my latest code onto this release tonight or tomorrow.
Regards
Jaco Goussard Technical Director
Northgate Office Park
Block 8 Unit 96
Cnr Aureole and Profit Rd
Northriding
www.sypher.co.za
TEL:
(011) 462 4354
FAX:
(011) 462 5514
MOBILE:
+27 84 699 6000
E-MAIL:
Jaco@sypher.co.za
------ Original Message ------ From: "mda30" notifications@github.com To: "rudders/homebridge-http" homebridge-http@noreply.github.com Cc: "cybertza" jaco@sypher.co.za; "Mention" mention@noreply.github.com Sent: 2016/09/25 01:06:41 AM Subject: Re: [rudders/homebridge-http] socket hang up (#42)
@cybertza i am testing your fork now. Should i expect status to be up to date if I use "yes" and the device state is changed manually, or do I need realtime for that (in which case I will need to wait until you port the caching into 'realtime' to test most of my devices). Thanks.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
Hello, I’m very new to homeBridge and this forum. I hope it’s ok to post a question here.
After many many hours I finally almost finished installing Homebridge on my windows 10 pc.
After running command ‘node homebridge’ i Get this error HTTP get power function failed: connect ECONNREFUSED 127.9.0.1:80
ReferenceError: callback is not defined at C:\Users\wendy\AppData\Roaming\npm\node-modules\homebridge-http\index.js:115:4>
Can anybody tell me what I should do? I’m verry new to all of this.
Thanks in advance all!
That error would appear to be an issue with the device you are trying to talk to: HTTP get power function failed: connect ECONNREFUSED 127.9.0.1:80 It would seem that you are trying to ask 127.9.0.1 for a power status. Its hard to guess without your config, but i would start looking there.
Thanks for the reply,
How can I post my config so you can see it?
https://github.com/cybertza/homebridge-http/blob/master/sample-config.json
line 18 seems to be your problem from the sample config. All depending on the device you are trying to manage, and what the status replies.
I have t even made it that far yet, lol I’m just trying to get it to run on my PC.
I’m so lost here on coding and terms.....
But I hope if I’m persistent I’ll eventually figure it out.
I really would love to get all my devices running on homekit!
I am getting a socket hangup error several times a day that kills homebridge. I notice the hdmebridge-vera plug in dev branch is dealing with the same/similar issue right now. I'm not a js coder but is there anything I can do to help track down the issue? Thanks.
[9/20/2016, 10:46:53 PM] [Filter Pump] HTTP get power function failed: socket hang up /usr/local/lib/node_modules/homebridge-http/index.js:45 callback(error); ^
ReferenceError: callback is not defined at /usr/local/lib/node_modules/homebridge-http/index.js:45:19 at Request._callback (/usr/local/lib/node_modules/homebridge-http/index.js:115:4) at self.callback (/usr/local/lib/node_modules/homebridge-http/node_modules/request/request.js:187:22) at emitOne (events.js:96:13) at Request.emit (events.js:188:7) at Request.onRequestError (/usr/local/lib/node_modules/homebridge-http/node_modules/request/request.js:813:8) at emitOne (events.js:96:13) at ClientRequest.emit (events.js:188:7) at Socket.socketOnEnd (_http_client.js:344:9) at emitNone (events.js:91:20) at Socket.emit (events.js:185:7) at endReadableNT (_stream_readable.js:974:12) at _combinedTickCallback (internal/process/next_tick.js:74:11) at process._tickCallback (internal/process/next_tick.js:98:9)