Heatmiser Neohub

Begonnen von schnibberle, 24 Januar 2016, 17:05:55

Vorheriges Thema - Nächstes Thema

schnibberle

Hallo zusammen,

ich möchte gerne in diesem Thread meine Bemühungen ein eigenes Neohub Modul für FHEM zu schreiben dokumentieren.
Leider bin ich Perl-technisch nicht so bewandert, aber werde mein bestes versuchen.

Nachdem ich bislang nur mit einem "expect" Skript Daten vom Neohub abziehen konnte, hab ichs jetzt zumindest mal mit Perl geschafft.

Kurze Info zum Neohub :

- Neohub ist ein Produkt von Heatmiser (engl. Hersteller für Thermostate)
- Der Neohub ist eine kleine Box mit Ethernet-Anschluss als ZWave Basis für die NeoStat (230V Thermostate mit Display)
- Das ganze lässt sich auch per App über die Cloud steuern
- Heatmiser hat mir nach höflicher Nachfrage die API bereitgestellt.



1. Schritte -> Daten vom Neohub abzurufen

package main;

use strict;
use warnings;
use IO::Socket::INET;
use MIME::Base64;

my $socket = IO::Socket::INET->new(
    PeerAddr => '192.168.3.49',
    PeerPort => 4242,
    Proto => 'Tcp',
           Reuse    => 0,
           Timeout  => 9

);
die "Could not create socket: $!\n" unless $socket;
print "connected to the server\n";
my $info=qq("INFO");
my $room=qq("Bad");
my $get=qq("CURRENT_TEMPERATURE");
my $req = '{'.$info.':0}'.chr(0);
my $size = $socket->send($req);
print $req."\n";
print "sent data of length $size\n";

# notify server that request has been sent
# receive a response of up to 1024 characters from server
my $response = "";
my $response2 = "";
my $response3 = "";
$socket->recv($response,4096);
$socket->recv($response2,4096);
$socket->recv($response3,4096);
print "received response: $response";
print "$response2";
print "$response3";

$socket->close();



Und das Ergebnis
root@fhem:~# perl perl3.pl
connected to the server
{"INFO":0}
sent data of length 11
received response: {"devices":[{"AWAY":false,"COOLING":false,"COOLING_ENABLED":false,"COOLING_TEMPERATURE_IN_WHOLE_DEGREES":0,"COOL_INP":false,"COUNT_DOWN_TIME":"0:00","CRADLE_PAIRED_TO_REMOTE_SENSOR":false,"CRADLE_PAIRED_TO_STAT":false,"CURRENT_FLOOR_TEMPERATURE":255,"CURRENT_SET_TEMPERATURE":"16.0","CURRENT_TEMPERATURE":"20.6","DEMAND":false,"DEVICE_TYPE":1,"ENABLE_BOILER":false,"ENABLE_COOLING":false,"ENABLE_PUMP":false,"ENABLE_VALVE":false,"ENABLE_ZONE":false,"FAILSAFE_STATE":false,"FAIL_SAFE_ENABLED":false,"FLOOR_LIMIT":false,"FULL/PARTIAL_LOCK_AVAILABLE":false,"HEAT/COOL_MODE":false,"HEATING":false,"HOLD_TEMPERATURE":5,"HOLD_TIME":"0:00","HOLIDAY":false,"HOLIDAY_DAYS":0,"HUMIDITY":0,"LOCK":false,"LOCK_PIN_NUMBER":"9694","LOW_BATTERY":false,"MAX_TEMPERATURE":"21.0","MIN_TEMPERATURE":"20.0","MODULATION_LEVEL":0,"NEXT_ON_TIME":"7 days 22:00","OFFLINE":false,"OUPUT_DELAY":false,"OUTPUT_DELAY":0,"PREHEAT":false,"PREHEAT_TIME":"255:255","PROGRAM_MODE":"24HOURSFIXED","PUMP_DELAY":false,"RADIATORS_OR_UNDERFLOOR":false,"SENSOR_SELECTION":"BUILT_IN_AIR_SENSOR","SET_COUNTDOWN_TIME":0,"STANDBY":false,"STAT_MODE":{"MANUAL_OFF":true,"THERMOSTAT":true},"TEMPERATURE_FORMAT":false,"TEMP_HOLD":false,"TIMECLOCK_MODE":false,"TIMER":false,"TIME_CLOCK_OVERIDE_BIT":false,"VERSION_NUMBER":91,"WRITE_COUNT":5,"ZONE_1PAIRED_TO_MULTILINK":true,"ZONE_1_OR_2":false,"ZONE_2_PAIRED_TO_MULTILINK":false,"device":"Office"},{"AWAY":false,"COOLING":false,"COOLING_ENABLED":false,"COOLING_TEMPERATURE_IN_WHOLE_DEGREES":0,"COOL_INP":false,"COUNT_DOWN_TIME":"0:00","CRADLE_PAIRED_TO_REMOTE_SENSOR":false,"CRADLE_PAIRED_TO_STAT":false,"CURRENT_FLOOR_TEMPERATURE":255,"CURRENT_SET_TEMPERATURE":"16.0","CURRENT_TEMPERATURE":"20.5","DEMAND":false,"DEVICE_TYPE":1,"ENABLE_BOILER":false,"ENABLE_COOLING":false,"ENABLE_PUMP":false,"ENABLE_VALVE":false,"ENABLE_ZONE":false,"FAILSAFE_STATE":false,"FAIL_SAFE_ENABLED":false,"FLOOR_LIMIT":false,"FULL/PARTIAL_LOCK_AVAILABLE":false,"HEAT/COOL_MODE":false,"HEATING":false,"HOLD_TEMPERATURE":16,"HOLD_TIME":"71:10","HOLIDAY":false,"HOLIDAY_DAYS":0,"HUMIDITY":0,"LOCK":false,"LOCK_PIN_NUMBER":"9694","LOW_BATTERY":false,"MAX_TEMPERATURE":"21.0","MIN_TEMPERATURE":"19.0","MODULATION_LEVEL":0,"NEXT_ON_TIME":"3 days 16:24","OFFLINE":false,"OUPUT_DELAY":false,"OUTPUT_DELAY":0,"PREHEAT":false,"PREHEAT_TIME":"255:255","PROGRAM_MODE":"24HOURSFIXED","PUMP_DELAY":false,"RADIATORS_OR_UNDERFLOOR":false,"SENSOR_SELECTION":"BUILT_IN_AIR_SENSOR","SET_COUNTDOWN_TIME":0,"STANDBY":false,"STAT_MODE":{"MANUAL_OFF":true,"THERMOSTAT":true},"TEMPERATURE_FORMAT":false,"TEMP_HOLD":true,"TIMECLOCK_MODE":false,"TIMER":false,"TIME_CLOCK_OVERIDE_BIT":false,"VERSION_NUMBER":91,"WRITE_COUNT":11,"ZONE_1PAIRED_TO_MULTILINK":true,"ZONE_1_OR_2":false,"ZONE_2_PAIRED_TO_MULTILINK":false,"device":"Schlafzimmer"},{"AWAY":false,"COOLING":false,"COOLING_ENABLED":false,"COOLING_TEMPERATURE_IN_WHOLE_DEGREES":0,"COOL_INP":false,"COUNT_DOWN_TIME":"0:00","CRADLE_PAIRED_TO_REMOTE_SENSOR":false,"CRADLE_PAIRED_TO_STAT":false,"CURRENT_FLOOR_TEMPERATURE":255,"CURRENT_SET_TEMPERATURE":"20.0","CURRENT_TEMPERATURE":"20.2","DEMAND":false,"DEVICE_TYPE":1,"ENABLE_BOILER":false,"ENABLE_COOLING":false,"ENABLE_PUMP":false,"ENABLE_VALVE":false,"ENABLE_ZONE":false,"FAILSAFE_STATE":false,"FAIL_SAFE_ENABLED":false,"FLOOR_LIMIT":false,"FULL/PARTIAL_LOCK_AVAILABLE":false,"HEAT/COOL_MODE":false,"HEATING":false,"HOLD_TEMPERATURE":9,"HOLD_TIME":"0:00","HOLIDAY":false,"HOLIDAY_DAYS":0,"HUMIDITY":0,"LOCK":false,"LOCK_PIN_NUMBER":"9694","LOW_BATTERY":false,"MAX_TEMPERATURE":"21.0","MIN_TEMPERATURE":"18.0","MODULATION_LEVEL":0,"NEXT_ON_TIME":"7 days 22:00","OFFLINE":false,"OUPUT_DELAY":false,"OUTPUT_DELAY":0,"PREHEAT":false,"PREHEAT_TIME":"255:255","PROGRAM_MODE":"24HOURSFIXED","PUMP_DELAY":false,"RADIATORS_OR_UNDERFLOOR":false,"SENSOR_SELECTION":"BUILT_IN_AIR_SENSOR","SET_COUNTDOWN_TIME":0,"STANDBY":false,"STAT_MODE":{"MANUAL_OFF":true,"THERMOSTAT":true},"TEMPERATURE_FORMAT":false,"TEMP_HOLD":false,"TIMECLOCK_MODE":false,"TIMER":false,"TIME_CLOCK_OVERIDE_BIT":false,"VERSION_NUMBER":91,"WRITE_COUNT":5,"ZONE_1PAIRED_TO_MULTILINK":true,"ZONE_1_OR_2":false,"ZONE_2_PAIRED_TO_MULTILINK":false,"device":"Bad"},{"AWAY":false,"COOLING":false,"COOLING_ENABLED":false,"COOLING_TEMPERATURE_IN_WHOLE_DEGREES":0,"COOL_INP":false,"COUNT_DOWN_TIME":"0:00","CRADLE_PAIRED_TO_REMOTE_SENSOR":false,"CRADLE_PAIRED_TO_STAT":false,"CURRENT_FLOOR_TEMPERATURE":255,"CURRENT_SET_TEMPERATURE":"21.0","CURRENT_TEMPERATURE":"20.5","DEMAND":false,"DEVICE_TYPE":1,"ENABLE_BOILER":false,"ENABLE_COOLING":false,"ENABLE_PUMP":false,"ENABLE_VALVE":false,"ENABLE_ZONE":false,"FAILSAFE_STATE":false,"FAIL_SAFE_ENABLED":false,"FLOOR_LIMIT":false,"FULL/PARTIAL_LOCK_AVAILABLE":false,"HEAT/COOL_MODE":false,"HEATING":false,"HOLD_TEMPERATURE":21,"HOLD_TIME":"0:00","HOLIDAY":false,"HOLIDAY_DAYS":0,"HUMIDITY":0,"LOCK":false,"LOCK_PIN_NUMBER":"9694","LOW_BATTERY":false,"MAX_TEMPERATURE":"21.0","MIN_TEMPERATURE":"20.0","MODULATION_LEVEL":0,"NEXT_ON_TIME":"7 days 22:00","OFFLINE":false,"OUPUT_DELAY":false,"OUTPUT_DELAY":0,"PREHEAT":false,"PREHEAT_TIME":"255:255","PROGRAM_MODE":"24HOURSFIXED","PUMP_DELAY":false,"RADIATORS_OR_UNDERFLOOR":false,"SENSOR_SELECTION":"BUILT_IN_AIR_SENSOR","SET_COUNTDOWN_TIME":0,"STANDBY":false,"STAT_MODE":{"MANUAL_OFF":true,"THERMOSTAT":true},"TEMPERATURE_FORMAT":false,"TEMP_HOLD":false,"TIMECLOCK_MODE":false,"TIMER":false,"TIME_CLOCK_OVERIDE_BIT":false,"VERSION_NUMBER":91,"WRITE_COUNT":7,"ZONE_1PAIRED_TO_MULTILINK":true,"ZONE_1_OR_2":false,"ZONE_2_PAIRED_TO_MULTILINK":false,"device":"Ankleide"},{"AWAY":false,"COOLING":false,"COOLING_ENABLED":false,"COOLING_TEMPERATURE_IN_WHOLE_DEGREES":0,"COOL_INP":false,"COUNT_DOWN_TIME":"0:00","CRADLE_PAIRED_TO_REMOTE_SENSOR":false,"CRADLE_PAIRED_TO_STAT":false,"CURRENT_FLOOR_TEMPERATURE":255,"CURRENT_SET_TEMPERATURE":"24.0","CURRENT_TEMPERATURE":"21.9","DEMAND":false,"DEVICE_TYPE":1,"ENABLE_BOILER":false,"ENABLE_COOLING":false,"ENABLE_PUMP":false,"ENABLE_VALVE":false,"ENABLE_ZONE":false,"FAILSAFE_STATE":false,"FAIL_SAFE_ENABLED":false,"FLOOR_LIMIT":false,"FULL/PARTIAL_LOCK_AVAILABLE":false,"HEAT/COOL_MODE":false,"HEATING":true,"HOLD_TEMPERATURE":18,"HOLD_TIME":"0:00","HOLIDAY":false,"HOLIDAY_DAYS":0,"HUMIDITY":0,"LOCK":false,"LOCK_PIN_NUMBER":"9694","LOW_BATTERY":false,"MAX_TEMPERATURE":"23.0","MIN_TEMPERATURE":"21.0","MODULATION_LEVEL":0,"NEXT_ON_TIME":"7 days 22:00","OFFLINE":false,"OUPUT_DELAY":false,"OUTPUT_DELAY":0,"PREHEAT":false,"PREHEAT_TIME":"255:255","PROGRAM_MODE":"24HOURSFIXED","PUMP_DELAY":false,"RADIATORS_OR_UNDERFLOOR":false,"SENSOR_SELECTION":"BUILT_IN_AIR_SENSOR","SET_COUNTDOWN_TIME":0,"STANDBY":false,"STAT_MODE":{"MANUAL_OFF":true,"THERMOSTAT":true},"TEMPERATURE_FORMAT":false,"TEMP_HOLD":false,"TIMECLOCK_MODE":false,"TIMER":false,"TIME_CLOCK_OVERIDE_BIT":false,"VERSION_NUMBER":91,"WRITE_COUNT":9,"ZONE_1PAIRED_TO_MULTILINK":true,"ZONE_1_OR_2":false,"ZONE_2_PAIRED_TO_MULTILINK":false,"device":"Wohnzimmer"}]}
root@fhem:~#



Jetzt geht es darum die Einzelwerte zu extrahieren damit man Sie in Readings packen kann.

schnibberle

Die Temperatur wäre jetzt schonmal ausgelesen. Der Code ist noch alles andere als schön, aber zumindest bekommt man mal die Werte.

perl-Code
package main;

use strict;
use warnings;
use IO::Socket::INET;
use MIME::Base64;
use JSON;
use Data::Dumper;
use Data::Printer;
my $socket = IO::Socket::INET->new(
    PeerAddr => '192.168.3.49',
    PeerPort => 4242,
    Proto => 'Tcp',
           Reuse    => 0,
           Timeout  => 9

);
die "Could not create socket: $!\n" unless $socket;
my $info=qq("INFO");
my $room=qq("Bad");
my $get=qq("CURRENT_TEMPERATURE");
my $req = '{'.$info.':0}'.chr(0);
my $size = $socket->send($req);
my $response = "";
my $response2 = "";
my $response3 = "";
my $response4 = "";
my $result = "";
$socket->recv($response,8192);
$socket->recv($response2,8192);
$socket->recv($response3,8192);
$socket->recv($response4,8192);
sleep(1);
$result = join("",$response,$response2,$response3,$response4);
eval {
  decode_json($result);
  print "NO ERROR\n";
  my @perl = decode_json($result);
my %hash = %{$perl[0]};
my @array = $hash{"devices"};
my @device = $array[0][2];
my %hashs = %{$device[0]};
print "Device:".$hashs{"device"}."\n";
print "Temp:".$hashs{"CURRENT_TEMPERATURE"}."\n";
$socket->close();
} or do {
  print "ERROR\n";
};


Output
root@fhem:~# perl perl3.pl
ERROR
root@fhem:~# perl perl3.pl
NO ERROR
Device:Bad
Temp:17.7
root@fhem:~#



Problem ist noch ein wenig der recv-Buffer des IO-Sockets. Der scheint nicht immer den ganzen JSON Reply zu bekommen.

schnibberle

Mal wieder was neues von der Neohub Front
use strict;
use warnings;
use IO::Socket::INET;
use MIME::Base64;
use JSON;
use Data::Dumper;
use Data::Printer;
my $socket = IO::Socket::INET->new(
    PeerAddr => '192.168.3.49',
    PeerPort => 4242,
    Proto => 'Tcp',
           blocking    => 0,
           Timeout  => 2

);
die "Could not create socket: $!\n" unless $socket;
my $info=qq("INFO");
my $req = '{'.$info.':0}'.chr(0);
p $req;
my $size=$socket->send($req);
  my $buffer;
while(1)
{
my $char;
$socket->recv($char,3);
   $buffer .= $char;
   if($char eq "}]}")
        {
                last;
        }
}
p $buffer;
close $socket;


Mit diesem Code bekomme ich die API verlässlich angesprochen, jetzt gehts darum das in schöne Readings zu verpacken.

schnibberle

use strict;
use warnings;
use IO::Socket::INET;
use MIME::Base64;
use JSON;
use Data::Dumper;
use Data::Printer;
my $socket = IO::Socket::INET->new(
    PeerAddr => '192.168.3.49',
    PeerPort => 4242,
    Proto => 'Tcp',
           blocking    => 0,
           Timeout  => 2

);
die "Could not create socket: $!\n" unless $socket;
my $info=qq("INFO");
my $req = '{'.$info.':0}'.chr(0);
p $req;
my $size=$socket->send($req);
  my $buffer;
while(1)
{
my $char;
$socket->recv($char,3);
   $buffer .= $char;
   if($char eq "}]}")
{
last;
}
}
my $decoded;;
$decoded = decode_json($buffer);
my @friends = @{ $decoded->{'devices'} };
foreach my $f ( @friends ) {
  print "Raum:" . $f->{"device"} . " - Temp:" . $f->{"CURRENT_TEMPERATURE"} . " - Set Temp:" . $f->{"CURRENT_SET_TEMPERATURE"} . "\n";
}
close $socket;



Output :

root@fhem:~/Neohub# perl perl9.pl
"{"INFO":0}\0"
Raum:Office - Temp:21.5 - Set Temp:16.0
Raum:Schlafzimmer - Temp:21.1 - Set Temp:16.0
Raum:Bad - Temp:21.0 - Set Temp:17.0
Raum:Ankleide - Temp:21.3 - Set Temp:16.0
Raum:Wohnzimmer - Temp:22.5 - Set Temp:16.0
root@fhem:~/Neohub#


Es wird :)

schnibberle

Die 1. Alpha des NEOHUB Moduls :) Da ich nur sehr rudimentär Perl programmieren kann, ist das sicherlich der beste Code aber ich versuche mich ranzutasten.

Diese Version kann zumindest mal die Temperatur, Soll-Temperatur sowie den Heizstatus anzeigen.

root@fhem:~/Neohub# cat /opt/fhem/FHEM/99_NEOHUB.pm
package main;
use strict;
use warnings;
use IO::Socket::INET;
use JSON;

my %NEOHUB_sets = (
"mintemp" => "TextField",
"maxtemp" => "TextField",
"settemp" => "TextField"
);

sub NEOHUB_Initialize($) {
    my ($hash) = @_;

    $hash->{DefFn}      = 'NEOHUB_Define';
    $hash->{UndefFn}    = 'NEOHUB_Undef';
    $hash->{SetFn}      = 'NEOHUB_Set';
    $hash->{GetFn}      = 'NEOHUB_Get';
    $hash->{GetUpdate}  = 'NEOHUB_GetUpdate';
    $hash->{AttrFn}     = 'NEOHUB_Attr';
    $hash->{ReadFn}     = 'NEOHUB_Read';
    $hash->{AttrList} = 'none';
}
sub NEOHUB_Define($$) {
    my ($hash, $def) = @_;
    my @param = split('[ \t][ \t]*', $def);
   
    if(int(@param) < 4) {
        return "too few parameters: define <name> NEOHUB <ip> <interval>";
    }
    $hash->{name}  = $param[0];
    $hash->{IP} = $param[2];
    $hash->{INTERVAL} = $param[3];
    RemoveInternalTimer($hash);
    InternalTimer(gettimeofday()+$hash->{INTERVAL}, "NEOHUB_GetUpdate", $hash, 0);
    NEOHUB_Get($hash,$hash->{NAME},"status");
    return undef;
}


sub NEOHUB_GetUpdate($) {
    my ($hash) = @_;
    my $name = $hash->{NAME};
    InternalTimer(gettimeofday()+$hash->{INTERVAL}, "NEOHUB_GetUpdate", $hash, 1) if ($hash->{INTERVAL});
    Log3 $name, 5, "NEOHUB: GetUpdate";
    NEOHUB_Get($hash,$name,"status") if ($hash->{INTERVAL});
    return undef;
}


sub NEOHUB_Undef($$) {
    my ($hash, $arg) = @_;
    # nothing to do
    RemoveInternalTimer($hash);
    return undef;
}

sub NEOHUB_Get($@) {
my ($hash, @param) = @_;
my $name = shift @param;
my $room = shift @param;
Log3 $name, 5, "NEOHUB: ".$hash->{IP};
my $neohubsocket = IO::Socket::INET->new(
    PeerAddr => $hash->{IP},
    PeerPort => 4242,
    Proto => 'Tcp',
        blocking    => 0,
        Timeout  => 2
);
my $info=qq("INFO");
my $req = '{'.$info.':0}'.chr(0);
Log3 $name, 5, "NEOHUB: Get: Open Socket now";
my $size=$neohubsocket->send($req);
  my $buffer;
my $i=1;
        Log3 $name, 5, "NEOHUB: Get: Do while loop for all Neohub devices";
while(1)
{
my $char;
$i++;
$neohubsocket->recv($char,3);
    $buffer .= $char;
Log3 $name, 5, "NEOHUB:".$char;
if(index($buffer,"]}") ne "-1")
{
Log3 $name, 5, "NEOHUB: Get: Update last value";
last;
}
}
Log3 $name, 5, "NEOHUB: Get: All data retrieved";
my $decoded;
$decoded = decode_json($buffer);
my @info = @{ $decoded->{'devices'} };
Log3 $name, 5, "NEOHUB: Get: Now starting to update readings";
foreach my $thermostat ( @info ) {
        readingsBeginUpdate($hash);
my $readingName1 = $thermostat->{"device"} . "_temp";
my $readingName2 = $thermostat->{"device"} . "_heating";
my $readingName3 = $thermostat->{"device"} . "_settemp";
                my $wert1 = $thermostat->{"CURRENT_TEMPERATURE"};
                my $wert2 = $thermostat->{"HEATING"};
my $wert3 = $thermostat->{"CURRENT_SET_TEMPERATURE"};
        readingsBulkUpdate($hash, $readingName1, $wert1 );
readingsBulkUpdate($hash, $readingName2, $wert2 );
readingsBulkUpdate($hash, $readingName3, $wert3 );
        readingsEndUpdate($hash, 1);
}
close $neohubsocket;
return undef;
}

sub NEOHUB_Set($@) {
my ($hash, @param) = @_;

return '"set NEOHUB" needs at least two arguments <devicename> <value>' if (int(@param) < 2);

my $name = shift @param;
my $device = shift @param;
my $set = shift(@param);
my $value = shift(@param);
        Log 5, "NEOHUB: Set:".$name. " - ".$device." - ".$set." - ".$value;
return undef;
}


sub NEOHUB_Attr(@) {
my ($cmd,$name,$attr_name,$attr_value) = @_;
if($cmd eq "set") {
        if($attr_name eq "formal") {
if($attr_value !~ /^yes|no$/) {
    my $err = "Invalid argument $attr_value to $attr_name. Must be yes or no.";
    Log 3, "NEOHUB: ".$err;
    return $err;
}
} else {
    return "Unknown attr $attr_name";
}
}
return undef;
}

1;

=pod
=begin html

<a name="NEOHUB"></a>
<h3>NEOHUB</h3>
<ul>
    <i>NEOHUB</i> implements the control of Heatmiser Neohub Thermostats.
    <br><br>
    <a name="NEOHUBdefine"></a>
    <b>Define</b>
    <ul>
        <code>define <name> NEOHUB <ip> <interval></code>
        <br><br>
        Example: <code>define NEOHUB MyHub 192.168.1.5 5</code>
        <br><br>
        The "IP" parameter is the IP address of the Neohub box.<br>
        The "interval" parameter defines the interval of updates requested from the Neohub<br>
    </ul>
    <br>
   
    <a name="NEOHUBset"></a>
    <b>Set</b><br>
    <ul>
        <code>set <name> <option> <value></code>
        <br><br>
        You can <i>set</i> any value to any of the following options. They're just there to
        <i>get</i> them. See <a href="http://fhem.de/commandref.html#set">commandref#set</a>
        for more info about the set command.
        <br><br>
        Options:
        <ul>
              <li><i>satisfaction</i><br>
                  Defaults to "no"</li>
              <li><i>whatyouwant</i><br>
                  Defaults to "can't"</li>
              <li><i>whatyouneed</i><br>
                  Defaults to "try sometimes"</li>
        </ul>
    </ul>
    <br>

    <a name="Helloget"></a>
    <b>Get</b><br>
    <ul>
        <code>get <name> <option></code>
        <br><br>
        You can <i>get</i> the value of any of the options described in
        <a href="#Helloset">paragraph "Set" above</a>. See
        <a href="http://fhem.de/commandref.html#get">commandref#get</a> for more info about
        the get command.
    </ul>
    <br>
   
    <a name="Helloattr"></a>
    <b>Attributes</b>
    <ul>
        <code>attr <name> <attribute> <value></code>
        <br><br>
        See <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> for more info about
        the attr command.
        <br><br>
        Attributes:
        <ul>
            <li><i>formal</i> no|yes<br>
                When you set formal to "yes", all output of <i>get</i> will be in a
                more formal language. Default is "no".
            </li>
        </ul>
    </ul>
</ul>

=end html

=cut
root@fhem:~/Neohub#


Ein passendes FTUI Widget habe ich mir auch schon zusammengebaut
<li data-row="5" data-col="2" data-sizex="1" data-sizey="1">
  <header>HEIZUNG WOHN.</header>
<div class="container">
        <div class="left">
                 <div data-type="thermostat" data-device="Heizung" data-set="settemp Wohnzimmer" data-get="Wohnzimmer_temp" data-temp="Wohnzimmer_temp" data-angleArc="180" data-angleOffset="270" class="cell top-space-2x">
        </div></div>
        <div class="container" style="position:absolute;z-index:2;">
                <div data-type="label">&nbsp;</div>
                <div data-type="label" data-device="Heizung" data-get="Wohnzimmer_temp" data-unit=" °C" class="darker top-narrow left-space-3x"></div>
                </div>
    </div>
        <div class="container" style="position:absolute;z-index:3;">
                <div data-type="label">&nbsp;</div>
                <div data-type="symbol" data-device="Heizung" data-icon="fa-fire" data-get-on="true" data-get-off="false" data-get="Wohnzimmer_heating" class="top-narrow-2x"></div>
        </div>
</li>


FTUI Widget
(http://s2.postimg.org/alg299po9/neohub_widget.png)

schnibberle

Jetzt auch mit Möglichkeit zum setzen der Zieltemperatur
set Heizung settemp Wohnzimmer 20

root@fhem:/opt/fhem/FHEM# cat 99_NEOHUB.pm
package main;
use strict;
use warnings;
use IO::Socket::INET;
use JSON;

my %NEOHUB_sets = (
"mintemp" => "TextField",
"maxtemp" => "TextField",
"settemp" => "TextField"
);

sub NEOHUB_Initialize($) {
    my ($hash) = @_;

    $hash->{DefFn}      = 'NEOHUB_Define';
    $hash->{UndefFn}    = 'NEOHUB_Undef';
    $hash->{SetFn}      = 'NEOHUB_Set';
    $hash->{GetFn}      = 'NEOHUB_Get';
    $hash->{GetUpdate}  = 'NEOHUB_GetUpdate';
    $hash->{AttrFn}     = 'NEOHUB_Attr';
    $hash->{ReadFn}     = 'NEOHUB_Read';
    $hash->{AttrList} = 'none';
}
sub NEOHUB_Define($$) {
    my ($hash, $def) = @_;
    my @param = split('[ \t][ \t]*', $def);
   
    if(int(@param) < 4) {
        return "too few parameters: define <name> NEOHUB <ip> <interval>";
    }
    $hash->{name}  = $param[0];
    $hash->{IP} = $param[2];
    $hash->{INTERVAL} = $param[3];
    RemoveInternalTimer($hash);
    InternalTimer(gettimeofday()+$hash->{INTERVAL}, "NEOHUB_GetUpdate", $hash, 0);
    NEOHUB_Get($hash,$hash->{NAME},"status");
    return undef;
}


sub NEOHUB_GetUpdate($) {
    my ($hash) = @_;
    my $name = $hash->{NAME};
    InternalTimer(gettimeofday()+$hash->{INTERVAL}, "NEOHUB_GetUpdate", $hash, 1) if ($hash->{INTERVAL});
    Log3 $name, 5, "NEOHUB: GetUpdate";
    NEOHUB_Get($hash,$name,"status") if ($hash->{INTERVAL});
    return undef;
}


sub NEOHUB_Undef($$) {
    my ($hash, $arg) = @_;
    # nothing to do
    RemoveInternalTimer($hash);
    return undef;
}

sub NEOHUB_Get($@) {
my ($hash, @param) = @_;
my $name = shift @param;
my $room = shift @param;
Log3 $name, 5, "NEOHUB: ".$hash->{IP};
my $neohubsocket = IO::Socket::INET->new(
    PeerAddr => $hash->{IP},
    PeerPort => 4242,
    Proto => 'Tcp',
        blocking    => 0,
        Timeout  => 2
);
my $info=qq("INFO");
my $req = '{'.$info.':0}'.chr(0);
Log3 $name, 5, "NEOHUB: Get: Open Socket now";
my $size=$neohubsocket->send($req);
  my $buffer;
my $i=1;
        Log3 $name, 5, "NEOHUB: Get: Do while loop for all Neohub devices";
while(1)
{
my $char;
$i++;
$neohubsocket->recv($char,3);
    $buffer .= $char;
Log3 $name, 5, "NEOHUB:".$char;
if(index($buffer,"]}") ne "-1")
{
Log3 $name, 5, "NEOHUB: Get: Update last value";
last;
}
}
Log3 $name, 5, "NEOHUB: Get: All data retrieved";
my $decoded;
$decoded = decode_json($buffer);
my @info = @{ $decoded->{'devices'} };
Log3 $name, 5, "NEOHUB: Get: Now starting to update readings";
foreach my $thermostat ( @info ) {
        readingsBeginUpdate($hash);
my $readingName1 = $thermostat->{"device"} . "_temp";
my $readingName2 = $thermostat->{"device"} . "_heating";
my $readingName3 = $thermostat->{"device"} . "_settemp";
                my $wert1 = $thermostat->{"CURRENT_TEMPERATURE"};
                my $wert2 = $thermostat->{"HEATING"};
my $wert3 = $thermostat->{"CURRENT_SET_TEMPERATURE"};
        readingsBulkUpdate($hash, $readingName1, $wert1 );
readingsBulkUpdate($hash, $readingName2, $wert2 );
readingsBulkUpdate($hash, $readingName3, $wert3 );
        readingsEndUpdate($hash, 1);
}
close $neohubsocket;
return undef;
}

sub NEOHUB_Set($@) {
my ($hash, @param) = @_;

return '"set NEOHUB" needs at least three arguments <devicename> <action> <thermostatname> <value>' if (int(@param) < 3);

my $name = shift @param;
my $action = shift @param;
my $thermostat = shift(@param);
my $value = shift(@param);
        Log 5, "NEOHUB: Set:".$name. " - ".$action." -".$thermostat." - ".$value;
if($action eq "settemp")
{
        my $neohubsocket = IO::Socket::INET->new(
        PeerAddr => "192.168.3.49",
        PeerPort => 4242,
        Proto => 'Tcp',
        blocking    => 0,
        Timeout  => 2
        );
        my $req = '{'.qq("SET_TEMP").':['.$value.','.qq("$thermostat").']}'.chr(0);
        my $size=$neohubsocket->send($req);
}
return undef;
}


sub NEOHUB_Attr(@) {
my ($cmd,$name,$attr_name,$attr_value) = @_;
if($cmd eq "set") {
        if($attr_name eq "formal") {
if($attr_value !~ /^yes|no$/) {
    my $err = "Invalid argument $attr_value to $attr_name. Must be yes or no.";
    Log 3, "NEOHUB: ".$err;
    return $err;
}
} else {
    return "Unknown attr $attr_name";
}
}
return undef;
}

1;

=pod
=begin html

<a name="NEOHUB"></a>
<h3>NEOHUB</h3>
<ul>
    <i>NEOHUB</i> implements the control of Heatmiser Neohub Thermostats.
    <br><br>
    <a name="NEOHUBdefine"></a>
    <b>Define</b>
    <ul>
        <code>define <name> NEOHUB <ip> <interval></code>
        <br><br>
        Example: <code>define NEOHUB MyHub 192.168.1.5 5</code>
        <br><br>
        The "IP" parameter is the IP address of the Neohub box.<br>
        The "interval" parameter defines the interval of updates requested from the Neohub<br>
    </ul>
    <br>
   
    <a name="NEOHUBset"></a>
    <b>Set</b><br>
    <ul>
        <code>set <name> <option> <value></code>
        <br><br>
        You can <i>set</i> any value to any of the following options. They're just there to
        <i>get</i> them. See <a href="http://fhem.de/commandref.html#set">commandref#set</a>
        for more info about the set command.
        <br><br>
        Options:
        <ul>
              <li><i>satisfaction</i><br>
                  Defaults to "no"</li>
              <li><i>whatyouwant</i><br>
                  Defaults to "can't"</li>
              <li><i>whatyouneed</i><br>
                  Defaults to "try sometimes"</li>
        </ul>
    </ul>
    <br>

    <a name="Helloget"></a>
    <b>Get</b><br>
    <ul>
        <code>get <name> <option></code>
        <br><br>
        You can <i>get</i> the value of any of the options described in
        <a href="#Helloset">paragraph "Set" above</a>. See
        <a href="http://fhem.de/commandref.html#get">commandref#get</a> for more info about
        the get command.
    </ul>
    <br>
   
    <a name="Helloattr"></a>
    <b>Attributes</b>
    <ul>
        <code>attr <name> <attribute> <value></code>
        <br><br>
        See <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> for more info about
        the attr command.
        <br><br>
        Attributes:
        <ul>
            <li><i>formal</i> no|yes<br>
                When you set formal to "yes", all output of <i>get</i> will be in a
                more formal language. Default is "no".
            </li>
        </ul>
    </ul>
</ul>

=end html

=cut
root@fhem:/opt/fhem/FHEM#