Initial version
This commit is contained in:
154
HASS.pm
Normal file
154
HASS.pm
Normal file
@@ -0,0 +1,154 @@
|
||||
package Plugins::Assistant::HASS;
|
||||
|
||||
use strict;
|
||||
use JSON::XS::VersionOneAndTwo;
|
||||
use threads::shared;
|
||||
|
||||
use Slim::Networking::SimpleAsyncHTTP;
|
||||
use Slim::Utils::Log;
|
||||
use Slim::Utils::Prefs;
|
||||
|
||||
my $log = logger('plugin.assistant');
|
||||
|
||||
my $url;
|
||||
my $cache;
|
||||
|
||||
my $prefs = preferences('plugin.assistant');
|
||||
|
||||
|
||||
sub init {
|
||||
($cache) = @_;
|
||||
|
||||
$url = $prefs->get('connect');
|
||||
|
||||
#$pass = $prefs->get('pass');
|
||||
}
|
||||
|
||||
|
||||
sub testHass {
|
||||
|
||||
# TODO:
|
||||
# empty api request
|
||||
# if response message = "API running." then return true
|
||||
# show in settings
|
||||
}
|
||||
|
||||
|
||||
sub getEntities {
|
||||
my ( $client, $cb, $params, $args ) = @_;
|
||||
|
||||
our $result :shared = [];
|
||||
our $counter :shared = 0;
|
||||
|
||||
if (defined $args->{'entity_ids'}) {
|
||||
foreach my $entity_id(@{$args->{'entity_ids'}}) {
|
||||
|
||||
$counter++;
|
||||
Plugins::Assistant::HASS::getEntity(
|
||||
$client,
|
||||
sub {
|
||||
my $entity = shift;
|
||||
if (defined $entity) {
|
||||
push @$result, $entity;
|
||||
}
|
||||
$counter--;
|
||||
if ($counter <= 0) {
|
||||
$cb->($result);
|
||||
}
|
||||
},
|
||||
$params,
|
||||
{
|
||||
entity_id => $entity_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
Plugins::Assistant::HASS::getEntity(
|
||||
$client,
|
||||
sub {
|
||||
my $entities = shift;
|
||||
foreach my $entity(@$entities) {
|
||||
push @$result, $entity;
|
||||
}
|
||||
$cb->($result);
|
||||
},
|
||||
$params,
|
||||
{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub getEntity {
|
||||
my ($client, $cb, $params, $args) = @_;
|
||||
|
||||
my $localurl = $url.'states';
|
||||
if (defined $args->{'entity_id'}) {
|
||||
$localurl = $localurl.'/'.$args->{'entity_id'};
|
||||
}
|
||||
|
||||
$log->debug('Get Entity: ', $localurl);
|
||||
|
||||
my $http = Slim::Networking::SimpleAsyncHTTP->new(
|
||||
sub {
|
||||
my $response = shift;
|
||||
my $params = $response->params('params');
|
||||
my $result;
|
||||
if ( $response->headers->content_type =~ /json/ ) {
|
||||
$result = decode_json($response->content);
|
||||
}
|
||||
$cb->($result);
|
||||
},
|
||||
sub {
|
||||
$log->warn("Warning (".$localurl."): $_[1]");
|
||||
$cb->();
|
||||
},
|
||||
{
|
||||
params => $params,
|
||||
timeout => 5,
|
||||
},
|
||||
);
|
||||
|
||||
$http->get(
|
||||
$localurl,
|
||||
'Content-Type' => 'application/json',
|
||||
'charset' => 'UTF-8',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
sub toggleLightEntity {
|
||||
my ($client, $cb, $params, $args) = @_;
|
||||
|
||||
my $localurl = $url.'services/light/toggle';
|
||||
my $req->{'entity_id'} = $args->{'entity_id'};
|
||||
|
||||
my $http = Slim::Networking::SimpleAsyncHTTP->new(
|
||||
sub {
|
||||
my $response = shift;
|
||||
my $params = $response->params('params');
|
||||
my $result;
|
||||
if ( $response->headers->content_type =~ /json/ ) {
|
||||
$result = decode_json($response->content);
|
||||
}
|
||||
$cb->($result);
|
||||
},
|
||||
sub {
|
||||
$log->error("error: $_[1]");
|
||||
$cb->();
|
||||
},
|
||||
{
|
||||
timeout => 5,
|
||||
},
|
||||
);
|
||||
|
||||
$http->post(
|
||||
$localurl,
|
||||
'Content-Type' => 'application/json',
|
||||
'charset' => 'UTF-8',
|
||||
encode_json($req)
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
BIN
HTML/EN/plugins/Assistant/html/images/icon.png
Normal file
BIN
HTML/EN/plugins/Assistant/html/images/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
BIN
HTML/EN/plugins/Assistant/html/images/light_off.png
Normal file
BIN
HTML/EN/plugins/Assistant/html/images/light_off.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 858 B |
BIN
HTML/EN/plugins/Assistant/html/images/light_on.png
Normal file
BIN
HTML/EN/plugins/Assistant/html/images/light_on.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 713 B |
11
HTML/EN/plugins/Assistant/settings.html
Normal file
11
HTML/EN/plugins/Assistant/settings.html
Normal file
@@ -0,0 +1,11 @@
|
||||
[% PROCESS settings/header.html %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_ASSISTANT_CONNECT" desc="PLUGIN_ASSISTANT_CONNECT_DESC" %]
|
||||
<input type="text" name="pref_connect" value="[% prefs.connect == '_assistant_' ? '' : prefs.connect %]" size="40">
|
||||
[% END %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_ASSISTANT_PASS" desc="PLUGIN_ASSISTANT_PASS_DESC" %]
|
||||
<input type="text" name="pref_pass" value="[% prefs.pass == '_pass_' ? '' : prefs.pass %]">
|
||||
[% END %]
|
||||
|
||||
[% PROCESS settings/footer.html %]
|
||||
203
Plugin.pm
Normal file
203
Plugin.pm
Normal file
@@ -0,0 +1,203 @@
|
||||
package Plugins::Assistant::Plugin;
|
||||
|
||||
use strict;
|
||||
use base qw(Slim::Plugin::OPMLBased);
|
||||
use JSON::XS::VersionOneAndTwo;
|
||||
use threads::shared;
|
||||
use feature qw(switch);
|
||||
|
||||
use Slim::Utils::Log;
|
||||
use Slim::Utils::Prefs;
|
||||
use Slim::Utils::Strings qw(string cstring);
|
||||
|
||||
use Plugins::Assistant::HASS;
|
||||
|
||||
my $log = Slim::Utils::Log->addLogCategory(
|
||||
{
|
||||
'category' => 'plugin.assistant',
|
||||
'defaultLevel' => 'DEBUG',
|
||||
'description' => 'PLUGIN_ASSISTANT',
|
||||
}
|
||||
);
|
||||
|
||||
my $prefs = preferences('plugin.assistant');
|
||||
|
||||
my $cache = Slim::Utils::Cache->new('assistant', 3);
|
||||
|
||||
|
||||
sub initPlugin {
|
||||
my $class = shift;
|
||||
|
||||
if (my $username = $prefs->get('connect')) {
|
||||
$prefs->set('connect', '') if $username eq '_assistant_';
|
||||
}
|
||||
|
||||
$prefs->init(
|
||||
{
|
||||
connect => '_assistant_'
|
||||
}
|
||||
);
|
||||
|
||||
Plugins::Assistant::HASS->init($cache);
|
||||
|
||||
$class->SUPER::initPlugin(
|
||||
feed => \&handleFeed,
|
||||
tag => 'assistant',
|
||||
menu => 'radios',
|
||||
is_app => 1,
|
||||
weight => 1,
|
||||
);
|
||||
|
||||
if (main::WEBUI) {
|
||||
require Plugins::Assistant::Settings;
|
||||
Plugins::Assistant::Settings->new();
|
||||
}
|
||||
}
|
||||
|
||||
sub getDisplayName { 'PLUGIN_ASSISTANT' }
|
||||
|
||||
# don't add this plugin to the Extras menu
|
||||
sub playerMenu {}
|
||||
|
||||
|
||||
sub handleFeed {
|
||||
my ($client, $cb, $args) = @_;
|
||||
|
||||
my $params = $args->{params};
|
||||
|
||||
# Only groups in first level
|
||||
$args->{'onlygroups'} = 1;
|
||||
|
||||
getItems($client,$cb,$params,$args);
|
||||
}
|
||||
|
||||
|
||||
sub getItems {
|
||||
my ($client, $cb, $params, $args) = @_;
|
||||
|
||||
Plugins::Assistant::HASS::getEntities(
|
||||
$client,
|
||||
sub {
|
||||
my $entities = shift;
|
||||
my $items = [];
|
||||
|
||||
foreach my $entity(@$entities) {
|
||||
my ($namespace, $name) = split('\.', $entity->{'entity_id'}, 2);
|
||||
|
||||
my $order = 999;
|
||||
if (defined $entity->{'attributes'}->{'order'}) {
|
||||
$order = $entity->{'attributes'}->{'order'};
|
||||
}
|
||||
|
||||
# If current entity is included in args and this is a group,
|
||||
# change namespace to the namespace of all sub entities
|
||||
# Note: Currently only light is supported
|
||||
if ($namespace eq 'group' && $entity->{'entity_id'} eq $args->{'entity_id'}) {
|
||||
$namespace = 'light';
|
||||
}
|
||||
|
||||
$log->debug('Namespace: ', $namespace, ' Name: ', $name, ' - ', $order);
|
||||
|
||||
if ($namespace eq 'group' && (!$entity->{'attributes'}->{'hidden'} || $entity->{'attributes'}->{'view'})) {
|
||||
|
||||
# Add current to request list if all sub entities the same
|
||||
# Add current entity id to args
|
||||
# Note: Currently only light is supported
|
||||
my $entity_ids = $entity->{'attributes'}->{'entity_id'};
|
||||
if (!grep(!/light\./, @{$entity_ids})) {
|
||||
push @$entity_ids, $entity->{'entity_id'};
|
||||
}
|
||||
|
||||
push @$items,
|
||||
{
|
||||
name => $entity->{'attributes'}->{'friendly_name'},
|
||||
order => $order,
|
||||
type => 'link',
|
||||
url => \&getItems,
|
||||
passthrough => [
|
||||
{
|
||||
entity_id => $entity->{'entity_id'},
|
||||
entity_ids => $entity_ids,
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
} elsif ($namespace eq 'light' && !defined $args->{'onlygroups'}) {
|
||||
|
||||
push @$items,
|
||||
{
|
||||
name => $entity->{'attributes'}->{'friendly_name'},
|
||||
image => 'plugins/Assistant/html/images/light_'.$entity->{'state'}.'.png',
|
||||
order => $order,
|
||||
type => 'link',
|
||||
url => \&toggleLightEntity,
|
||||
passthrough => [
|
||||
{
|
||||
entity_id => $entity->{'entity_id'},
|
||||
state => $entity->{'state'},
|
||||
}
|
||||
],
|
||||
nextWindow => 'refresh',
|
||||
};
|
||||
|
||||
} elsif ($namespace eq 'sensor' && !defined $args->{'onlygroups'}) {
|
||||
|
||||
push @$items,
|
||||
{
|
||||
name => $entity->{'attributes'}->{'friendly_name'}.' '.$entity->{'state'}.$entity->{'attributes'}->{'unit_of_measurement'},
|
||||
order => $order,
|
||||
type => 'text',
|
||||
};
|
||||
|
||||
} elsif (!defined $args->{'onlygroups'}) {
|
||||
|
||||
push @$items,
|
||||
{
|
||||
name => $entity->{'attributes'}->{'friendly_name'}.' '.$entity->{'state'},
|
||||
order => $order,
|
||||
type => 'text',
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
$items = [ sort { uc($a->{order}) cmp uc($b->{order}) } @$items ];
|
||||
$cb->(
|
||||
{
|
||||
items => $items,
|
||||
}
|
||||
);
|
||||
},
|
||||
$params,
|
||||
{
|
||||
entity_ids => $args->{'entity_ids'},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
sub toggleLightEntity {
|
||||
my ($client, $cb, $params, $args) = @_;
|
||||
|
||||
Plugins::Assistant::HASS::toggleLightEntity(
|
||||
$client,
|
||||
sub {
|
||||
my $items = [];
|
||||
|
||||
push @$items,
|
||||
{
|
||||
name => 'Toggled Light',
|
||||
type => 'text',
|
||||
showBriefly => 1,
|
||||
};
|
||||
$cb->(
|
||||
{
|
||||
items => $items,
|
||||
}
|
||||
);
|
||||
},
|
||||
$params,
|
||||
$args,
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
25
Settings.pm
Normal file
25
Settings.pm
Normal file
@@ -0,0 +1,25 @@
|
||||
package Plugins::Assistant::Settings;
|
||||
|
||||
use strict;
|
||||
use base qw(Slim::Web::Settings);
|
||||
|
||||
use Slim::Utils::Prefs;
|
||||
|
||||
my $prefs = preferences('plugin.assistant');
|
||||
|
||||
|
||||
sub name {
|
||||
return 'PLUGIN_ASSISTANT';
|
||||
}
|
||||
|
||||
|
||||
sub prefs {
|
||||
return ($prefs, 'connect');
|
||||
}
|
||||
|
||||
|
||||
sub page {
|
||||
return 'plugins/Assistant/settings.html';
|
||||
}
|
||||
|
||||
1;
|
||||
20
install.xml
Normal file
20
install.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version='1.0' standalone='yes'?>
|
||||
<extension>
|
||||
<name>PLUGIN_ASSISTANT</name>
|
||||
<creator>Hans Karlinius</creator>
|
||||
<defaultState>enabled</defaultState>
|
||||
<needsMySB>false</needsMySB>
|
||||
<description>PLUGIN_ASSISTANT_DESCRIPTION</description>
|
||||
<email>hans.karlinius@live.com</email>
|
||||
<icon>plugins/Assistant/html/images/icon.png</icon>
|
||||
<id>517d7aef-1bfa-49a2-b4ed-b0117d997531</id>
|
||||
<module>Plugins::Assistant::Plugin</module>
|
||||
<optionsURL>plugins/Assistant/settings.html</optionsURL>
|
||||
<targetApplication>
|
||||
<id>Logitech Media Server</id>
|
||||
<maxVersion>*</maxVersion>
|
||||
<minVersion>7.6</minVersion>
|
||||
</targetApplication>
|
||||
<type>2</type>
|
||||
<version>0.1</version>
|
||||
</extension>
|
||||
17
strings.txt
Normal file
17
strings.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
PLUGIN_ASSISTANT
|
||||
EN Assistant
|
||||
|
||||
PLUGIN_ASSISTANT_DESCRIPTION
|
||||
EN This is good
|
||||
|
||||
PLUGIN_ASSISTANT_CONNECT
|
||||
EN Home Assistant connect url
|
||||
|
||||
PLUGIN_ASSISTANT_CONNECT_DESC
|
||||
EN Should be the same as used for web access with addition of /api at the end like http://localhost:8123/api
|
||||
|
||||
PLUGIN_ASSISTANT_PASS
|
||||
EN Home Assistant API password
|
||||
|
||||
PLUGIN_ASSISTANT_PASS_DESC
|
||||
EN The password set for http request, when logging in to the webpage
|
||||
Reference in New Issue
Block a user