diff --git a/TODO b/TODO index 4e52298..8ee5091 100755 --- a/TODO +++ b/TODO @@ -1,5 +1,12 @@ * Add the event collection where the master node polls information status and triggers a specified method of a plugin * Plugins that handles physical situations must insert events in the collections to be called when necessary + +* Deeme: Emit hourly, daily, half-hour by an integrated cron daemon. + +* XXX: Aggiungere l'event emitter anche a StatusListener -> plugin aggiornamento di stato e Aggiungere l'event emitter anche all'rpc. + +*https://metacpan.org/pod/Mojo::EventEmitter -> making a Mojo::EventEmitter binded to Mongo db collection + * Split parsing modules (Parser::*) because events would require a different Parser (Unique, just changes the DB Backend) * With the avaibility of time, integrating Bread::Board diff --git a/bin/intellihome-deployer b/bin/intellihome-deployer index 6e4599e..af8d98a 100755 --- a/bin/intellihome-deployer +++ b/bin/intellihome-deployer @@ -25,12 +25,12 @@ my $Deployer = $Backend->new; eval { $Output->error("$Deployer hasn't $action") and die(EXIT_ERROR) unless ( $Deployer->can($action) ); - $Deployer->$action(@ARGV); }; if ($@) { $Output->error($@); } + $Deployer->$action(@ARGV); sub help { $Output->info( "At least an argument to --cmd it's required : [install,upgrade,prepare] " diff --git a/config/rpc.yml b/config/rpc.yml new file mode 100644 index 0000000..991f8a5 --- /dev/null +++ b/config/rpc.yml @@ -0,0 +1,4 @@ +--- +rpc_host: 'localhost' +rpc_port: '3000' + diff --git a/cpanfile b/cpanfile index 3ecf39c..1a63554 100755 --- a/cpanfile +++ b/cpanfile @@ -1,10 +1,16 @@ requires 'AnyEvent'; requires 'AnyEvent::Filesys::Notify'; requires 'Carp::Always'; +requires 'DBIx::Class::Core'; +requires 'DBIx::Class::DeploymentHandler'; +requires 'DBIx::Class::Schema'; requires 'Deeme'; requires 'Deeme::Backend::Mango'; +requires 'Deeme::Backend::Memory'; +requires 'Digest::SHA1'; requires 'Encode'; requires 'File::Find::Object'; +requires 'File::Path'; requires 'Getopt::Long'; requires 'LWP::UserAgent'; requires 'Log::Any'; @@ -22,7 +28,6 @@ requires 'Moo::Role'; requires 'MooX::Singleton'; requires 'Moose'; requires 'Moose::Role'; -requires 'MooseX::Singleton'; requires 'Net::SSH::Any'; requires 'Term::ANSIColor'; requires 'Time::HiRes'; @@ -31,12 +36,15 @@ requires 'URI'; requires 'Unix::PID'; requires 'YAML::Tiny'; requires 'feature'; +requires 'forks'; requires 'namespace::autoclean'; +requires 'perl', '5.010'; on configure => sub { requires 'ExtUtils::MakeMaker'; }; on test => sub { + requires 'MojoX::JSON::RPC::Client'; requires 'Test::More'; }; diff --git a/ex/insertdb.pl b/ex/insertdb.pl new file mode 100644 index 0000000..fa3d1ad --- /dev/null +++ b/ex/insertdb.pl @@ -0,0 +1,90 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use lib './lib'; +use IntelliHome::Schema::SQLite::Schema; + +my $schema = IntelliHome::Schema::SQLite::Schema->connect( + 'dbi:SQLite:/var/lib/intellihome/intellihome.db'); + +my $room_data = { + 'name' => "cucina", + 'location' => "cucina" +}; + +my $room = $schema->resultset('Room')->create($room_data); + +my $node_data1 = { + 'roomid' => $room->id, + 'name' => 'master', + 'host' => 'localhost', + 'port' => 23459, + 'type' => 'master', + 'username' => 'username', + 'password' => 'passwd' +}; + +my $node_data2 = { + 'roomid' => $room->id, + 'name' => 'master', + 'host' => '127.0.0.1', + 'port' => 23456, + 'type' => 'node', + 'username' => 'username', + 'password' => 'passwd' +}; + +my $node_one = $schema->resultset('Node')->create($node_data1); +my $node_two = $schema->resultset('Node')->create($node_data2); + +my $gpio_data1 = { + 'nodeid' => $node_two->id, + 'num_gpio' => 1, + 'type' => 3, + 'value' => 0, + 'driver' => "IntelliHome::Driver::GPIO::Mono" +}; + +my $gpio_data2 = { + 'nodeid' => $node_two->id, + 'num_gpio' => 2, + 'type' => 1, + 'value' => 1, + 'driver' => "IntelliHome::Driver::GPIO::Dual" +}; + +my $gpio_one = $schema->resultset('GPIO')->create($gpio_data1); +my $gpio_two = $schema->resultset('GPIO')->create($gpio_data2); + +my $tag_data1 = { + 'gpioid' => $gpio_one->id, + 'tag' => "serranda 1" +}; + +my $tag_data2 = { + 'gpioid' => $gpio_two->id, + 'tag' => "serranda 2" +}; + +my $tag_one = $schema->resultset('Tag')->create($tag_data1); +my $tag_two = $schema->resultset('Tag')->create($tag_data2); + +my $user_data1 = { + 'username' => "user1", + 'password' => "password", + 'name' => "User 1", + 'admin' => 0 +}; + +my $user_data2 = { + 'username' => "user2", + 'password' => "password", + 'name' => "User 2", + 'admin' => 1 +}; + +my $user_one = $schema->resultset('User')->create($user_data1); +my $user_two = $schema->resultset('User')->create($user_data2); + + diff --git a/lib/IntelliHome/Config.pm b/lib/IntelliHome/Config.pm index 22247c6..304558f 100755 --- a/lib/IntelliHome/Config.pm +++ b/lib/IntelliHome/Config.pm @@ -9,17 +9,20 @@ use Moo; with 'MooX::Singleton'; has 'Nodes' => ( is => "rw" ); +has 'RPCConfiguration' => ( is => "rw", default => sub { {} } ); has 'DBConfiguration' => ( is => "rw", default => sub { {} } ); -has 'Dirs' => ( is => "rw" ); +has 'Dirs' => ( is => "rw", default=>sub{['./config']}); has 'Output' => ( is => "rw", - default => sub { return IntelliHome::Interfaces::Terminal->instance} + default => sub { return IntelliHome::Interfaces::Terminal->instance } ); -sub BUILD{ - my $self=shift; + +sub BUILD { + my $self = shift; $self->read; } + sub read { my $self = shift; my $output = $self->Output; @@ -40,22 +43,28 @@ sub read { #Importing data in my hash with an index by host (for convenience) foreach my $Key ( @{$yaml} ) { - if ( exists( $Key->{db_dsn} ) - and exists( $Key->{db_name} ) - and exists( $Key->{database_backend} ) + if (exists( + $Key->{database_backend}) + and exists( $Key->{language} ) + # and exists($Key->{username}) # and exists ($Key->{password}) ) { - $self->DBConfiguration->{'db_dsn'} = $Key->{'db_dsn'}; - $output->info( "Database: " - . $self->DBConfiguration->{'db_dsn'} ); - $self->DBConfiguration->{'db_name'} - = $Key->{'db_name'}; - $output->info( "Database: " - . $self->DBConfiguration->{'db_name'} ); + if ( exists( $Key->{db_dsn} ) + and exists( $Key->{db_name} ) ) + { + $self->DBConfiguration->{'db_dsn'} + = $Key->{'db_dsn'}; + $output->info( "Database: " + . $self->DBConfiguration->{'db_dsn'} ); + $self->DBConfiguration->{'db_name'} + = $Key->{'db_name'}; + $output->info( "Database: " + . $self->DBConfiguration->{'db_name'} ); + } $self->DBConfiguration->{'database_backend'} = $Key->{'database_backend'}; $output->info( "Database backend: " @@ -125,6 +134,18 @@ sub read { = $Key->{mic_step} if ( exists( $Key->{mic_step} ) ); } + + if ( exists( $Key->{rpc_host} ) + and exists( $Key->{rpc_port} ) + ) + { + $self->RPCConfiguration->{'rpc_host'} = $Key->{'rpc_host'}; + $output->info( "RPC-Host: " + . $self->RPCConfiguration->{'rpc_host'} ); + $self->RPCConfiguration->{'rpc_port'} = $Key->{'rpc_port'}; + $output->info( "RPC-Port: " + . $self->RPCConfiguration->{'rpc_port'} ); + } } } diff --git a/lib/IntelliHome/Deployer/Schema/SQLite.pm b/lib/IntelliHome/Deployer/Schema/SQLite.pm new file mode 100644 index 0000000..86b882c --- /dev/null +++ b/lib/IntelliHome/Deployer/Schema/SQLite.pm @@ -0,0 +1,70 @@ +package IntelliHome::Deployer::Schema::SQLite; + +use strict; +use Carp::Always; +use warnings; +use 5.010; +use DBIx::Class::DeploymentHandler; +use lib '../../'; +use IntelliHome::Schema::SQLite::Schema; +use File::Path qw(make_path); +use constant DBDIR => "/var/lib/intellihome/"; +use Moo; + +has 'dh' => ( + is => "rw", + lazy => 1, + default => sub { + make_path DBDIR unless -d DBDIR; + return DBIx::Class::DeploymentHandler->new( + { schema => IntelliHome::Schema::SQLite::Schema->connect( + 'dbi:SQLite:' . DBDIR . 'intellihome.db' + ), + script_directory => DBDIR . 'db_upgrades', + databases => 'SQLite', + force_overwrite => 1, + schema_version => 1 #TODO pass version + } + ); + } +); + +sub prepare { + my $self = shift; + my $from_version = shift; + my $to_version = shift; + $self->dh->prepare_install; + + if ( defined $from_version && defined $to_version ) { + $self->dh->prepare_upgrade( + { from_version => $from_version, + to_version => $to_version, + } + ); + } +} + +sub install { + my $self = shift; + my $version = shift; + if ( defined $version ) { + $self->dh->install( { schema_version => $version } ); + } + else { + $self->dh->install; + } +} + +sub upgrade { + return shift->upgrade; +} + +sub database_version { + return shift->database_version; +} + +sub schema_version { + return shift->schema_version; +} + +1; \ No newline at end of file diff --git a/lib/IntelliHome/IntelliHomeNode.pm b/lib/IntelliHome/IntelliHomeNode.pm index fe0a88e..d19c4b0 100755 --- a/lib/IntelliHome/IntelliHomeNode.pm +++ b/lib/IntelliHome/IntelliHomeNode.pm @@ -8,6 +8,7 @@ require IntelliHome::Workers::Node::AudioProcess; require IntelliHome::Schema::YAML::Node; require IntelliHome::Workers::Node::MicAdjust; require AnyEvent; +use Carp::Always; use base qw(Exporter); use IntelliHome::Utils qw(daemonize cleanup); use Moo; diff --git a/lib/IntelliHome/Parser/DB/SQLite.pm b/lib/IntelliHome/Parser/DB/SQLite.pm new file mode 100644 index 0000000..7308b56 --- /dev/null +++ b/lib/IntelliHome/Parser/DB/SQLite.pm @@ -0,0 +1,129 @@ +package IntelliHome::Parser::DB::SQLite; +use Moose; +extends 'IntelliHome::Parser::DB::Base'; +use IntelliHome::Schema::SQLite::Schema; +use IntelliHome::Utils qw(load_module); + +has 'Schema' => ( is => "rw" ); + +sub BUILD { + my $self = shift; + $self->Schema( + IntelliHome::Schema::SQLite::Schema->connect( + 'dbi:SQLite:/var/lib/intellihome/intellihome.db') + ); +} + +sub search_gpio { + my $self = shift; + my $tag = shift; + return $self->Schema->resultset('GPIO')->search( + { + 'tag.tag' => $tag + }, + { + join => [qw/ gpioid /] + } + ); +} + +sub search_trigger { + my $self = shift; + my $trigger = shift; + return $self->Schema->resultset('Trigger')->search( + { + trigger => { 'like', '%' . $trigger . '%' }, + } + ); +} + +sub getTriggers { + my $self = shift; + return $self->Schema->resultset('Trigger')->all; +} + +sub installTrigger { + my $self = shift; + my $Options = shift; + my $Command = shift; + my $comm = $self->Schema->resultset('Command')->search( + { + command => $Command->{'command'}, + plugin => $Command->{'plugin'} + } + ); + return 0 unless ($comm); + my $Trigger = $self->Schema->resultset('Trigger') + ->search( { trigger => $Options->{'trigger'} } ); + return 0 unless ($Trigger); + my $new_trigger = $self->Schema->resultset('Trigger')->new( %{$Options} ); + $new_trigger->command( $comm->commandid ); + return $new_trigger->insert; +} + +sub removeTrigger { + my $self = shift; + my $Options = shift; + my $Trigger = $self->Schema->resultset('Trigger') + ->search( { trigger => $Options->{'trigger'} } )->delete_all; +} + +sub newHypo { + my $self = shift; + my $Hypos = shift; + +# return $_ if IntelliHome::Schema::Mongo::Hypo->find_one( {hypo => $Hypos->{hypo} }); + return IntelliHome::Schema::Mongo::Hypo->new( %{$Hypos} ); +} + +sub addTask { + my $self = shift; + my $Task = shift; + return IntelliHome::Schema::Mongo::Task->new( %{$Task} ); + +} + +sub addNode { + my $self = shift; + my $Options = shift; + my $Room = shift; + my $r = + $self->Schema->resultset('Room')->search( { name => $Room->{'name'} } ); + return 0 unless ($r); + my $Node = $self->Schema->resultset('Node') + ->search( { host => $Options->{'host'} } ); + return 0 unless ($Node); + my $new_node = $self->Schema->resultset('Node')->new( %{$Options} ); + $new_node->room( $r->roomid ); + return $new_node->insert; +} + +sub getNodes { + my $self = shift; + my $Query = shift; + return $self->Schema->resultset('Node')->all; +} + +sub getActiveTasks { + my $self = shift; + return IntelliHome::Schema::Mongo::Task->query( + { status => 1, node => shift->Host } )->all(); +} + +sub node { + return shift; +} + +sub selectFromHost { + return + shift->Schema->resultset('Node') + ->search( { Host => shift, type => shift } )->single; +} + +sub selectFromType { + return + shift->Schema->resultset('Node')->search( { type => shift } )->single; + +} + +1; diff --git a/lib/IntelliHome/Parser/SQLite.pm b/lib/IntelliHome/Parser/SQLite.pm new file mode 100644 index 0000000..9ca8206 --- /dev/null +++ b/lib/IntelliHome/Parser/SQLite.pm @@ -0,0 +1,103 @@ +package IntelliHome::Parser::SQLite; + +use Moo; +extends 'IntelliHome::Parser::Base'; +use IntelliHome::Parser::DB::SQLite; +use IntelliHome::Schema::SQLite::Schema; + +has 'Backend' => ( is => "rw" ); + +sub BUILD { + my $self = shift; + $self->Backend( + IntelliHome::Parser::DB::SQLite->new( Config => $self->Config ) ); +} + +#TODO substitute Mongo task with another kind of object. +# sub detectTasks { +# my $self = shift; +# my $Hypothesis = shift; +# my $hypo = $Hypothesis->hypo; +# my @Tasks = IntelliHome::Schema::Mongo::Task->query( +# { node => { host => $self->Node->Host }, status => 1 } )->all; +# if ( scalar @Tasks > 0 ) { + +# # $self->Output->info( "Ci sono " . scalar(@Tasks) . " task aperti" ); +# foreach my $Task (@Tasks) { + +# } + +# ##XXX: +# ## Se i task ci sono, vengono processati perchè si suppone questa submission dell'utente sia una parziale risposta +# ## Dunque, si riempie i dati del task precedente, +# ## così l'altro thread può terminare la richiesta e quindi la risposta (se eventualmente genera altri task, ci sarà un motivo) +# ## Si controllano comandi di tipologia di annullamento, in tal caso si pone il task in deletion così il thread si chiude. +# } +# else { +# # $self->Output->info( "nessun task per " . $self->Node->Host ); + +# } +# } + +sub detectTriggers { + my $self = shift; + my $hypo = shift; + + my ( $t, @args ) = split( " ", $hypo ); + my $rs = $self->Backend->search_trigger($t); + my $Satisfied = 0; + while ( my $trigger = $rs->next ) { + if ( @{ $trigger->{result} } = + join( " ", @args ) =~ /$trigger->arguments/i ) + { + $Satisfied++ + if $self->run_plugin( $trigger->command->plugin, + $trigger->command->plugin_method, $trigger ); + + # my $r = $item->regex; + # $hypo =~ s/$r//g; #removes the trigger + #Checking the trigger needs. + + # foreach my $need ( $item->needs->all ) { + # if ( $need->compile($hypo) ) { + + # $hypo =~ s/$r//g; #removes the trigger + # } + # elsif ( $need->forced ) { + + # #ASkUsers + # my @Q = $need->questions->all; + # caller->Output->info( + # $Q[ int( rand( scalar @Q ) ) ]->ask() ); + # ###XXX: + # ### QUI CREO IL TASK E ASPETTO UN CAMBIAMENTO DI STATO. + + # } + # } + + } + else { + # print "No match for trigger.\n"; + + } + + } + $self->Output->debug("A total of $Satisfied plugins satisfied the request"); + return $Satisfied; +} + +sub parse { + my $self = shift; + my $caller = caller; + my @hypotheses = @_; + + return 0 if scalar @hypotheses < 0; + + foreach my $hypo (@hypotheses) { + + last if $self->detectTriggers($hypo) != 0; + + } + +} +1; diff --git a/lib/IntelliHome/RPC/Service/Dummy.pm b/lib/IntelliHome/RPC/Service/Dummy.pm index 77cf7fb..4160880 100644 --- a/lib/IntelliHome/RPC/Service/Dummy.pm +++ b/lib/IntelliHome/RPC/Service/Dummy.pm @@ -3,14 +3,13 @@ package IntelliHome::RPC::Service::Dummy; #this is a dummy rpc module only for testing use Carp::Always; use Mojo::Base 'IntelliHome::RPC::Service::Base'; -use Data::Dumper; has 'IntelliHome'; sub dummy { my ( $self, @params ) = @_; - return Dumper($self)."DUMMY-YUMMY! "; + return "DUMMY-YUMMY!"; } __PACKAGE__->register_rpc_method_names('dummy'); diff --git a/lib/IntelliHome/Schema/SQLite/Schema.pm b/lib/IntelliHome/Schema/SQLite/Schema.pm new file mode 100644 index 0000000..442aa8a --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema.pm @@ -0,0 +1,6 @@ +package IntelliHome::Schema::SQLite::Schema; +use base qw/DBIx::Class::Schema/; + +__PACKAGE__->load_namespaces(); + +1; \ No newline at end of file diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/Command.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/Command.pm new file mode 100644 index 0000000..bff5404 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/Command.pm @@ -0,0 +1,15 @@ +package IntelliHome::Schema::SQLite::Schema::Result::Command; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('command'); +__PACKAGE__->add_columns( + 'commandid' => { data_type=>'int', is_auto_increment=>1 }, + 'name', + 'plugin', + 'command'); +__PACKAGE__->set_primary_key('commandid'); +__PACKAGE__->has_many(triggers => 'IntelliHome::Schema::SQLite::Schema::Result::Trigger', 'commandid'); +__PACKAGE__->has_many(commandgpio => 'IntelliHome::Schema::SQLite::Schema::Result::CommandGPIO', 'commandid'); +__PACKAGE__->many_to_many('gpios' => 'commandgpio', 'gpioid'); + +1; diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/CommandGPIO.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/CommandGPIO.pm new file mode 100644 index 0000000..d169d9a --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/CommandGPIO.pm @@ -0,0 +1,14 @@ +package IntelliHome::Schema::SQLite::Schema::Result::CommandGPIO; + +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('command_gpio'); +__PACKAGE__->add_columns( + 'id' =>{ data_type=>'int', is_auto_increment=>1}, + 'commandid' => { data_type=>'int'}, + 'gpioid' => { data_type=>'int'} ); +__PACKAGE__->set_primary_key('id'); +__PACKAGE__->belongs_to(command => 'IntelliHome::Schema::SQLite::Schema::Result::Command','commandid'); +__PACKAGE__->belongs_to(gpio => 'IntelliHome::Schema::SQLite::Schema::Result::GPIO','gpioid'); + +1; diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/GPIO.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/GPIO.pm new file mode 100644 index 0000000..963887c --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/GPIO.pm @@ -0,0 +1,20 @@ +package IntelliHome::Schema::SQLite::Schema::Result::GPIO; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('gpio'); +__PACKAGE__->add_columns( + 'gpioid' => { data_type=>'int', is_auto_increment=>1 }, + 'nodeid' => { data_type=>'int' }, + 'num_gpio', + 'type', + 'value', + 'driver' ); +__PACKAGE__->set_primary_key('gpioid'); +__PACKAGE__->belongs_to(node => 'IntelliHome::Schema::SQLite::Schema::Result::Node', 'nodeid'); +__PACKAGE__->has_many(usergpio => 'IntelliHome::Schema::SQLite::Schema::Result::UserGPIO', 'gpioid'); +__PACKAGE__->has_many(commandgpio => 'IntelliHome::Schema::SQLite::Schema::Result::CommandGPIO', 'gpioid'); +__PACKAGE__->has_many(tags => 'IntelliHome::Schema::SQLite::Schema::Result::Tag', 'gpioid'); +__PACKAGE__->many_to_many('users' => 'usergpio', 'userid'); +__PACKAGE__->many_to_many('commands' => 'commandgpio', 'commandid'); + +1; diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/Node.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/Node.pm new file mode 100644 index 0000000..058c8e8 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/Node.pm @@ -0,0 +1,34 @@ +package IntelliHome::Schema::SQLite::Schema::Result::Node; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('node'); +__PACKAGE__->add_columns( + 'nodeid' => { data_type => 'int', is_auto_increment => 1 }, + 'roomid' => { data_type => 'int' }, + 'name', + 'description' => { is_nullable => 1 }, + 'host', + 'port' => { data_type => 'int' }, + 'type', + 'username', + 'password' +); +__PACKAGE__->set_primary_key('nodeid'); +__PACKAGE__->has_many( + gpios => 'IntelliHome::Schema::SQLite::Schema::Result::GPIO', + 'nodeid' +); +__PACKAGE__->belongs_to( + room => 'IntelliHome::Schema::SQLite::Schema::Result::Room', + 'roomid' +); + +sub Host { + shift->host(@_); +} + +sub Port { + shift->port(@_); +} + +1; diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/Plugin.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/Plugin.pm new file mode 100644 index 0000000..adc1ed8 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/Plugin.pm @@ -0,0 +1,14 @@ +package IntelliHome::Schema::SQLite::Schema::Result::Plugin; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('plugin'); +__PACKAGE__->add_columns(qw/ pluginid name description gpioid commandid triggerid /); +__PACKAGE__->set_primary_key('pluginid'); +__PACKAGE__->has_many(userplugin => 'IntelliHome::Schema::SQLite::Schema::Result::UserPlugin', 'pluginid'); +__PACKAGE__->has_many(gpio => 'IntelliHome::Schema::SQLite::Schema::Result::GPIO', 'gpioid'); +__PACKAGE__->has_many(command => 'IntelliHome::Schema::SQLite::Schema::Result::Command', 'commandid'); +__PACKAGE__->has_many(trigger => 'IntelliHome::Schema::SQLite::Schema::Result::Trigger', 'triggerid'); +__PACKAGE__->many_to_many('users' => 'userplugin', 'userid'); + + +1; \ No newline at end of file diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/RemoteControlLayout.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/RemoteControlLayout.pm new file mode 100644 index 0000000..1def46b --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/RemoteControlLayout.pm @@ -0,0 +1,23 @@ +package IntelliHome::Schema::SQLite::Schema::Result::RemoteControlLayout; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('remote_control_layout'); +__PACKAGE__->add_columns( + 'rclid' => { data_type=>'int', is_auto_increment=>1 }, + 'userid' => { data_type=>'int' }, + 'name' => { accessor => '_check_name' }); +__PACKAGE__->set_primary_key('rclid'); +__PACKAGE__->belongs_to(user => 'IntelliHome::Schema::SQLite::Schema::Result::User', 'userid'); + + +sub check_name (@) { + my ($self, $value) = @_; + + die "Invalid name format!" if($value =~ /[$-\/:-?{-~!"^_`\[\]]|^$/); + #TODO convert the string in a standard form + $self->_check_name($value); + + return $self->_check_name(); +} + +1; diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/Room.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/Room.pm new file mode 100644 index 0000000..0f981d5 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/Room.pm @@ -0,0 +1,13 @@ +package IntelliHome::Schema::SQLite::Schema::Result::Room; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('room'); +__PACKAGE__->add_columns( + 'roomid' => { data_type=>'int', is_auto_increment=>1 }, + 'name', + 'location' => { is_nullable => 1} ); +__PACKAGE__->set_primary_key('roomid'); +__PACKAGE__->has_many(nodes => 'IntelliHome::Schema::SQLite::Schema::Result::Node','roomid'); + + +1; \ No newline at end of file diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/Tag.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/Tag.pm new file mode 100644 index 0000000..7459ff3 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/Tag.pm @@ -0,0 +1,14 @@ +package IntelliHome::Schema::SQLite::Schema::Result::Tag; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('tag'); +__PACKAGE__->add_columns( + 'tagid' => { data_type=>'int', is_auto_increment=>1 }, + 'gpioid' => { data_type=>'int' }, + 'tag', + 'description' => { is_nullable => 1 } ); +__PACKAGE__->set_primary_key('tagid'); +__PACKAGE__->belongs_to(gpio => 'IntelliHome::Schema::SQLite::Schema::Result::GPIO', 'gpioid'); + + +1; \ No newline at end of file diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/Trigger.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/Trigger.pm new file mode 100644 index 0000000..85cffe6 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/Trigger.pm @@ -0,0 +1,23 @@ +package IntelliHome::Schema::SQLite::Schema::Result::Trigger; +use Carp::Always; +use base qw/DBIx::Class::Core/; + + +sub new { + my $self=shift; + $self->SUPER::new(@_); + $self->{result} = []; + return $self; +} + +__PACKAGE__->table('trigger'); +__PACKAGE__->add_columns( + 'triggerid' => { data_type=>'int', is_auto_increment=>1 }, + 'commandid' => { data_type=>'int' }, + 'trigger', + 'arguments', + 'language' ); +__PACKAGE__->set_primary_key('triggerid'); +__PACKAGE__->belongs_to(command => 'IntelliHome::Schema::SQLite::Schema::Result::Command', 'commandid'); + +1; diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/User.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/User.pm new file mode 100644 index 0000000..5043c09 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/User.pm @@ -0,0 +1,41 @@ +package IntelliHome::Schema::SQLite::Schema::Result::User; +use base qw/DBIx::Class::Core/; +use Digest::SHA1 qw(sha1); + +__PACKAGE__->table('user'); +__PACKAGE__->add_columns( + 'userid' => { data_type=>'int', is_auto_increment=>1 }, + 'username' => { accessor => '_check_username' }, + 'password' => { accessor => '_check_password', data_type=>'varchar', size => 40 }, + 'name' => { data_type=>'varchar'} , + 'picture' => { is_nullable => 1}, + 'admin' => { data_type => 'int', size=>1 }); +__PACKAGE__->set_primary_key('userid'); +__PACKAGE__->has_many(remotecontrollayouts => 'IntelliHome::Schema::SQLite::Schema::Result::RemoteControlLayout', 'userid'); +__PACKAGE__->has_many(usergpio => 'IntelliHome::Schema::SQLite::Schema::Result::UserGPIO', 'userid'); +__PACKAGE__->has_many(userroom => 'IntelliHome::Schema::SQLite::Schema::Result::UserRoom', 'userid'); +__PACKAGE__->many_to_many('gpios' => 'usergpio', 'gpioid'); +__PACKAGE__->many_to_many('rooms' => 'userroom', 'roomid'); + +sub check_username (@) { + my ($self, $value) = @_; + + die "Invalid username format!" if($value =~ /^$|\s+|[!$%^&*()+|~=`{}\[\]:";'<>?,\/]/); + $self->_check_username($value); + + return $self->_check_username(); +} + +sub check_password (@) { + my ($self, $value) = @_; + + die "Invalid password format!" if($value =~ /^$|^\s+$/); + $digest = sha1($value); + $self->_check_password($digest); + + return $self->_check_password(); +} + + + +1; diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/UserGPIO.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/UserGPIO.pm new file mode 100644 index 0000000..eefffda --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/UserGPIO.pm @@ -0,0 +1,13 @@ +package IntelliHome::Schema::SQLite::Schema::Result::UserGPIO; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('user_gpio'); +__PACKAGE__->add_columns( + 'id' =>{ data_type=>'int', is_auto_increment=>1}, + 'userid' => { data_type=>'int'}, + 'gpioid' => { data_type=>'int'} ); +__PACKAGE__->set_primary_key('id'); +__PACKAGE__->belongs_to(user => 'IntelliHome::Schema::SQLite::Schema::Result::User','userid'); +__PACKAGE__->belongs_to(gpio => 'IntelliHome::Schema::SQLite::Schema::Result::GPIO','gpioid'); + +1; \ No newline at end of file diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/UserPlugin.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/UserPlugin.pm new file mode 100644 index 0000000..d496d52 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/UserPlugin.pm @@ -0,0 +1,10 @@ +package IntelliHome::Schema::SQLite::Schema::Result::UserPlugin; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('user_plugin'); +__PACKAGE__->add_columns(qw/ id userid pluginid /); +__PACKAGE__->set_primary_key('id'); +__PACKAGE__->belongs_to(user => 'IntelliHome::Schema::SQLite::Schema::Result::User','userid'); +__PACKAGE__->belongs_to(plugin => 'IntelliHome::Schema::SQLite::Schema::Result::Plugin','pluginid'); + +1; \ No newline at end of file diff --git a/lib/IntelliHome/Schema/SQLite/Schema/Result/UserRoom.pm b/lib/IntelliHome/Schema/SQLite/Schema/Result/UserRoom.pm new file mode 100644 index 0000000..c43be09 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/Schema/Result/UserRoom.pm @@ -0,0 +1,10 @@ +package IntelliHome::Schema::SQLite::Schema::Result::UserRoom; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('user_room'); +__PACKAGE__->add_columns(qw/ id userid roomid /); +__PACKAGE__->set_primary_key('id'); +__PACKAGE__->belongs_to(user => 'IntelliHome::Schema::SQLite::Schema::Result::User','userid'); +__PACKAGE__->belongs_to(room => 'IntelliHome::Schema::SQLite::Schema::Result::Room','roomid'); + +1; \ No newline at end of file diff --git a/lib/IntelliHome/Schema/SQLite/deploy.pl b/lib/IntelliHome/Schema/SQLite/deploy.pl new file mode 100644 index 0000000..27cbee8 --- /dev/null +++ b/lib/IntelliHome/Schema/SQLite/deploy.pl @@ -0,0 +1,95 @@ +#!/usr/bin/env perl + +use strict; +use Carp::Always; +use warnings; +use 5.010; +use DBIx::Class::DeploymentHandler; +use feature qw/ switch /; +use Getopt::Long; +use lib '../../../'; +use IntelliHome::Schema::SQLite::Schema; +my $schema = IntelliHome::Schema::SQLite::Schema->connect('dbi:SQLite:intellihome.db'); + +my $cmd = ''; +my $from_version; +my $to_version; +my $version; +GetOptions( + 'command|cmd|c=s' => \$cmd, + 'from-version=i' => \$from_version, + 'to-version=i' => \$to_version, + 'version=i' => \$version, +); + +sub usage { + say <<'HERE'; +usage: + database.pl --cmd prepare [ --from-version $from --to-version $to ] + database.pl --cmd install [ --version $version ] + database.pl --cmd upgrade + database.pl --cmd database-version + database.pl --cmd schema-version +HERE + exit(0); +} + +#my $schema = '...'; # wherever you get your schema from. +my $deployment_handler_dir = './db_upgrades'; + +my $dh = DBIx::Class::DeploymentHandler->new( + { schema => $schema, + script_directory => $deployment_handler_dir, + databases => 'SQLite', + force_overwrite => 1, + schema_version => $version + } +); + +#die "We only support positive integers for versions." +# unless $version and $dh->schema_version =~ /^\d+$/; + +for ($cmd) { + when ('prepare') { prepare() } + when ('install') { install() } + when ('upgrade') { upgrade() } + when ('database-version') { database_version() } + when ('schema-version') { schema_version() } + default { usage() } +} + +sub prepare { + say "running prepare_install()"; + $dh->prepare_install; + + if ( defined $from_version && defined $to_version ) { + say + "running prepare_upgrade({ from_version => $from_version, to_version => $to_version })"; + $dh->prepare_upgrade( + { from_version => $from_version, + to_version => $to_version, + } + ); + } +} + +sub install { + if ( defined $version ) { + $dh->install({ schema_version => $version }); + } + else { + $dh->install; + } +} + +sub upgrade { + $dh->upgrade; +} + +sub database_version { + say $dh->database_version; +} + +sub schema_version { + say $dh->schema_version; +} diff --git a/lib/IntelliHome/Workers/Node/Sox.pm b/lib/IntelliHome/Workers/Node/Sox.pm index 6b25b01..fbb2a3e 100644 --- a/lib/IntelliHome/Workers/Node/Sox.pm +++ b/lib/IntelliHome/Workers/Node/Sox.pm @@ -29,7 +29,6 @@ has 'beginThreshold' => ( is => "rw", default => '1%' ); has 'finishEnable' => ( is => "rw", default => "1" ); has 'finishSoundDuration' => ( is => "rw", default => "1.5" ); has 'finishThreshold' => ( is => "rw", default => '2%' ); -has 'Directory' => ( is => "rw", default => "/var/tmp/sox/" ); has 'Filters' => ( is => "rw", default => "trim 0 12" diff --git a/lib/IntelliHome/Workers/Process.pm b/lib/IntelliHome/Workers/Process.pm index 6697030..022b059 100755 --- a/lib/IntelliHome/Workers/Process.pm +++ b/lib/IntelliHome/Workers/Process.pm @@ -40,8 +40,8 @@ has 'Writer' => ( is => "rw" ); has 'Reader' => ( is => "rw" ); has 'Error' => ( is => "rw" ); -has 'UnixPid' => ( is => "rw", default => sub { return new Unix::PID; } ); -has 'Directory' => ( is => "rw", default => "/var/tmp" ); +has 'UnixPid' => ( is => "rw", default => sub { return Unix::PID->new; } ); +has 'Directory' => ( is => "rw", default => "/var/tmp/intellihome/" ); sub launch { my $self = shift; diff --git a/lib/IntelliHome/Workers/Thread.pm b/lib/IntelliHome/Workers/Thread.pm index 6b01ad9..33d4e12 100755 --- a/lib/IntelliHome/Workers/Thread.pm +++ b/lib/IntelliHome/Workers/Thread.pm @@ -33,12 +33,17 @@ return L C on the thread =cut use Moo::Role; -use threads ( - 'yield', - 'stack_size' => 64 * 4096, - 'exit' => 'threads_only', - 'stringify' -); + +use Config; + +if ( $Config{usethreads} ) { + require threads; + import threads; +} +else { + require forks; + forks->import(); +} #Or you want to use forks? use Carp qw( croak ); @@ -76,6 +81,15 @@ sub stop { } } +sub signal { + my $self = shift; + my $signal = shift; + if ( defined $self->thread and !$self->thread->is_detached ) { + $self->thread->kill($signal); + } + return $self->thread; +} + sub is_running { shift->thread->is_running(); } sub is_detached { shift->thread->is_detached(); } diff --git a/t/5-rpc-dummy.t b/t/5-rpc-dummy.t new file mode 100644 index 0000000..56d5ff8 --- /dev/null +++ b/t/5-rpc-dummy.t @@ -0,0 +1,44 @@ +use Test::More; +use MojoX::JSON::RPC::Client; +use IntelliHome::RPC::Service::Dummy; +use IntelliHome::Workers::Master::RPC; + +my $client = MojoX::JSON::RPC::Client->new; +my $url = 'http://localhost:3000/dummy'; +my $callobj = { + id => 1, + method => 'dummy', + params => ["apri serranda"] +}; + +my $RPC = IntelliHome::Workers::Master::RPC->new(); +$RPC->launch; +sleep(4); +$client->call( + $url, $callobj, + sub { + # With callback + my $res = pop; + my $output; + + if ($res) { + if ( $res->is_error ) { # RPC ERROR + $output = 'Error : ', $res->error_message; + } + else { + $output = $res->result; + } + } + else { + my $tx_res = $client->tx->res; # Mojo::Message::Response object + $output + = 'HTTP response ' . $tx_res->code . ' ' . $tx_res->message; + } + is( $output, "DUMMY-YUMMY!", "rpc-answer" ); + $RPC->signal("TERM")->detach; + Mojo::IOLoop->stop; + } +); + +Mojo::IOLoop->start; +done_testing;