Initial commit
39
addon.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import xbmc
|
||||||
|
import xbmcgui
|
||||||
|
import xbmcaddon
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
# Load the settings
|
||||||
|
ADDON = xbmcaddon.Addon()
|
||||||
|
API_URL = ADDON.getSetting('api_url')
|
||||||
|
|
||||||
|
def send_request(endpoint, data):
|
||||||
|
url = f"{API_URL}/{endpoint}"
|
||||||
|
data = json.dumps(data).encode('utf-8')
|
||||||
|
req = urllib.request.Request(url, data, headers={'Content-Type': 'application/json'})
|
||||||
|
response = urllib.request.urlopen(req)
|
||||||
|
return json.loads(response.read().decode('utf-8'))
|
||||||
|
|
||||||
|
class AmplifierControlDialog(xbmcgui.WindowXMLDialog):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AmplifierControlDialog, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def onInit(self):
|
||||||
|
#self.getControl(100).setLabel("Volume")
|
||||||
|
#self.getControl(200).setLabel("Source")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onClick(self, controlId):
|
||||||
|
if controlId == 100:
|
||||||
|
volume = self.getControl(101).getValue()
|
||||||
|
send_request('set_volume', {'volume': volume})
|
||||||
|
elif controlId == 200:
|
||||||
|
source = self.getControl(201).getSelectedItem().getLabel()
|
||||||
|
send_request('set_source', {'source': source})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
dialog = AmplifierControlDialog('amplifier_control.xml', ADDON.getAddonInfo('path'), 'default', '720p')
|
||||||
|
dialog.doModal()
|
||||||
|
del dialog
|
14
addon.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<addon id="script.hathoris" name="Hathoris" version="1.0.0" provider-name="nemunaire">
|
||||||
|
<requires>
|
||||||
|
<import addon="xbmc.python" version="3.0.0" />
|
||||||
|
</requires>
|
||||||
|
<extension point="xbmc.python.script" library="lib/main.py">
|
||||||
|
<provides>executable</provides>
|
||||||
|
</extension>
|
||||||
|
<extension point="xbmc.addon.metadata">
|
||||||
|
<summary>Control your amplifier through a REST API</summary>
|
||||||
|
<description>Allows you to control volume, source, and other settings of your amplifier via a REST API.</description>
|
||||||
|
<platform>all</platform>
|
||||||
|
</extension>
|
||||||
|
</addon>
|
144
lib/main.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import xbmc
|
||||||
|
import xbmcgui
|
||||||
|
import xbmcaddon
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Initialize the logger
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ADDON = xbmcaddon.Addon()
|
||||||
|
ADDONID = xbmcaddon.Addon().getAddonInfo('id')
|
||||||
|
CWD = xbmcaddon.Addon().getAddonInfo('path')
|
||||||
|
API_URL = ADDON.getSetting('api_url')
|
||||||
|
|
||||||
|
CANCEL_DIALOG = (9, 10, 92, 216, 247, 257, 275, 61467, 61448)
|
||||||
|
|
||||||
|
def send_request(endpoint, data=None):
|
||||||
|
url = f"{API_URL}{endpoint}"
|
||||||
|
if data is not None:
|
||||||
|
data = json.dumps(data).encode('utf-8')
|
||||||
|
req = urllib.request.Request(url, data, headers={'Content-Type': 'application/json'})
|
||||||
|
response = urllib.request.urlopen(req)
|
||||||
|
if response.status > 300:
|
||||||
|
res = json.loads(response.read().decode('utf-8'))
|
||||||
|
xbmcgui.Dialog().notification('Error', f"Failed to create dialog: {res['errmsg']}", xbmcgui.NOTIFICATION_ERROR)
|
||||||
|
raise Exception(res["errmsg"])
|
||||||
|
return json.loads(response.read().decode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
|
# Sources ##########################################################
|
||||||
|
|
||||||
|
def get_sources():
|
||||||
|
sources = send_request("/sources")
|
||||||
|
res = []
|
||||||
|
for src in sources:
|
||||||
|
source = sources[src]
|
||||||
|
source["id"] = src
|
||||||
|
res.append(source)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_current_source():
|
||||||
|
sources = send_request("/sources")
|
||||||
|
for src in sources:
|
||||||
|
source = sources[src]
|
||||||
|
if source["enabled"]:
|
||||||
|
return sources[src]["name"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def enable_source(src):
|
||||||
|
send_request("/sources/" + src + "/enable", {})
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# Mixer ############################################################
|
||||||
|
|
||||||
|
def get_mixers():
|
||||||
|
return send_request("/mixer")
|
||||||
|
|
||||||
|
def mixer_value(mixer):
|
||||||
|
if mixer["Type"] == "ENUMERATED":
|
||||||
|
return str(mixer["items"][mixer["values"][0]])
|
||||||
|
elif mixer["Type"] == "BOOLEAN":
|
||||||
|
return "Oui" if mixer["items"][mixer["values"][0]] else "Non"
|
||||||
|
elif mixer["Type"] == "INTEGER" and "DBScale" in mixer:
|
||||||
|
return "%0.1f dB" % (mixer["DBScale"]["Min"] + mixer["DBScale"]["Step"] * mixer["values"][0])
|
||||||
|
else:
|
||||||
|
return str(mixer["values"][0])
|
||||||
|
|
||||||
|
def set_mixer(mixer, value):
|
||||||
|
val = []
|
||||||
|
for v in mixer["values"]:
|
||||||
|
val.append(value)
|
||||||
|
send_request("/mixer/%s/values" % mixer["NumID"], val)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class AmplifierControlDialog(xbmcgui.WindowXMLDialog):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AmplifierControlDialog, self).__init__(*args, **kwargs)
|
||||||
|
self.data = kwargs['optional1']
|
||||||
|
|
||||||
|
def onInit(self):
|
||||||
|
self.getControl(2).setLabel("Hathoris")
|
||||||
|
|
||||||
|
self.list = self.getControl(10)
|
||||||
|
self.list.reset()
|
||||||
|
self.list.addItem(xbmcgui.ListItem("Source", get_current_source()))
|
||||||
|
|
||||||
|
self.mixers = get_mixers()
|
||||||
|
for mixer in self.mixers:
|
||||||
|
self.list.addItem(xbmcgui.ListItem(mixer["Name"], mixer_value(mixer)))
|
||||||
|
|
||||||
|
#self.getControl(100).setLabel("Volume")
|
||||||
|
#self.getControl(200).setLabel("Source")
|
||||||
|
return
|
||||||
|
|
||||||
|
def onClick(self, controlId):
|
||||||
|
if controlId == 10:
|
||||||
|
if self.list.getSelectedPosition() == 0:
|
||||||
|
sources = get_sources()
|
||||||
|
selected = xbmcgui.Dialog().select('Sélectionnez la source à utiliser', [s["name"] for s in sources])
|
||||||
|
if selected != -1:
|
||||||
|
enable_source(sources[selected]["id"])
|
||||||
|
self.onInit()
|
||||||
|
elif self.mixers[self.list.getSelectedPosition() - 1]["Type"] == "ENUMERATED":
|
||||||
|
items = self.mixers[self.list.getSelectedPosition() - 1]["items"]
|
||||||
|
selected = xbmcgui.Dialog().select(self.mixers[self.list.getSelectedPosition() - 1]["Name"], items, preselect=self.mixers[self.list.getSelectedPosition() - 1]["values"][0])
|
||||||
|
if selected != -1:
|
||||||
|
set_mixer(self.mixers[self.list.getSelectedPosition() - 1], selected)
|
||||||
|
self.onInit()
|
||||||
|
elif self.mixers[self.list.getSelectedPosition() - 1]["Type"] == "BOOLEAN":
|
||||||
|
items = ["Non", "Oui"]
|
||||||
|
selected = xbmcgui.Dialog().select(self.mixers[self.list.getSelectedPosition() - 1]["Name"], items, preselect=1 if self.mixers[self.list.getSelectedPosition() - 1]["values"][0] else 0)
|
||||||
|
if selected != -1:
|
||||||
|
set_mixer(self.mixers[self.list.getSelectedPosition() - 1], True if selected else False)
|
||||||
|
self.onInit()
|
||||||
|
else:
|
||||||
|
newval = xbmcgui.Dialog().numeric(0, self.mixers[self.list.getSelectedPosition() - 1]["Name"], str(self.mixers[self.list.getSelectedPosition() - 1]["values"][0]))
|
||||||
|
set_mixer(self.mixers[self.list.getSelectedPosition() - 1], int(newval))
|
||||||
|
self.onInit()
|
||||||
|
elif controlId == 100:
|
||||||
|
volume = self.getControl(101).getValue()
|
||||||
|
send_request('set_volume', {'volume': volume})
|
||||||
|
elif controlId == 200:
|
||||||
|
source = self.getControl(201).getSelectedItem().getLabel()
|
||||||
|
send_request('set_source', {'source': source})
|
||||||
|
elif controlId == 18:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def onAction(self, action):
|
||||||
|
if action.getButtonCode() in CANCEL_DIALOG:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
dialog = AmplifierControlDialog('amplifier_control.xml', CWD, 'default', optional1='some data')
|
||||||
|
dialog.doModal()
|
||||||
|
del dialog
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to create dialog: {e}")
|
||||||
|
xbmcgui.Dialog().notification('Error', f"Failed to create dialog: {e}", xbmcgui.NOTIFICATION_ERROR)
|
110
resources/language/resource.language.en_GB/strings.po
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# Kodi Media Center language file
|
||||||
|
# Addon Name: RSS Editor
|
||||||
|
# Addon id: script.rss.editor
|
||||||
|
# Addon Provider: Team Kodi
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
"Project-Id-Version: XBMC Addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
|
||||||
|
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: Kodi Translation Team\n"
|
||||||
|
"Language-Team: English (http://www.transifex.com/projects/p/xbmc-addons/language/en/)\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Language: en\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
msgctxt "#32000"
|
||||||
|
msgid "RSS Editor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32001"
|
||||||
|
msgid "Change Set"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32002"
|
||||||
|
msgid "items"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32003"
|
||||||
|
msgid "Pages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32006"
|
||||||
|
msgid "Edit Attributes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32012"
|
||||||
|
msgid "RSS Feed URL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32013"
|
||||||
|
msgid "Update Interval"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32014"
|
||||||
|
msgid "Enter the paths of your RSS feeds %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32024"
|
||||||
|
msgid "Select a Set "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32025"
|
||||||
|
msgid "Set ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32027"
|
||||||
|
msgid "Enable Right to Left Text for this Set?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32030"
|
||||||
|
msgid "Set Editor"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32040"
|
||||||
|
msgid "Error: "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32041"
|
||||||
|
msgid "was either not found in the system,"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32042"
|
||||||
|
msgid "or does not contain parsable XML."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32043"
|
||||||
|
msgid "Try deleting it from your userdata folder and restarting XBMC."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32045"
|
||||||
|
msgid "Cannot Delete Set 1"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32046"
|
||||||
|
msgid "Set 1 is required. You cannot delete this."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32047"
|
||||||
|
msgid "Reset set 1 to default?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32050"
|
||||||
|
msgid "Set %s already exists."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32051"
|
||||||
|
msgid "Your RSSFeeds.xml file is corrupt."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32052"
|
||||||
|
msgid "Attempt to regenerate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#32053"
|
||||||
|
msgid "You will loose all previous settings"
|
||||||
|
msgstr ""
|
5
resources/settings.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<settings>
|
||||||
|
<category label="General">
|
||||||
|
<setting id="api_url" type="text" label="API URL" default="https://hathor.ra.nemunai.re/api" />
|
||||||
|
</category>
|
||||||
|
</settings>
|
135
resources/skins/default/1080i/amplifier_control.xml
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<window>
|
||||||
|
<defaultcontrol always="true">10</defaultcontrol>
|
||||||
|
<coordinates>
|
||||||
|
<left>350</left>
|
||||||
|
<top>135</top>
|
||||||
|
</coordinates>
|
||||||
|
<controls>
|
||||||
|
<control type="image">
|
||||||
|
<left>0</left>
|
||||||
|
<top>0</top>
|
||||||
|
<width>1220</width>
|
||||||
|
<height>712</height>
|
||||||
|
<texture>hathoris-panel.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<left>0</left>
|
||||||
|
<top>0</top>
|
||||||
|
<width>1220</width>
|
||||||
|
<height>70</height>
|
||||||
|
<texture colordiffuse="FF12B2E7" border="2">hathoris-header.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="label" id="2">
|
||||||
|
<textoffsetx>70</textoffsetx>
|
||||||
|
<left>-30</left>
|
||||||
|
<top>0</top>
|
||||||
|
<width>1220</width>
|
||||||
|
<height>70</height>
|
||||||
|
<font>font32_title</font>
|
||||||
|
<align>left</align>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<shadowcolor>black</shadowcolor>
|
||||||
|
</control>
|
||||||
|
<control type="list" id="10">
|
||||||
|
<left>20</left>
|
||||||
|
<top>80</top>
|
||||||
|
<onup>10</onup>
|
||||||
|
<ondown>10</ondown>
|
||||||
|
<onleft>9001</onleft>
|
||||||
|
<onright>9001</onright>
|
||||||
|
<itemgap>-20</itemgap>
|
||||||
|
<scrolltime>200</scrolltime>
|
||||||
|
<itemlayout height="69">
|
||||||
|
<control type="image">
|
||||||
|
<left>0</left>
|
||||||
|
<top>0</top>
|
||||||
|
<width>880</width>
|
||||||
|
<height>2</height>
|
||||||
|
<texture>hathoris-list-separator.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<left>0</left>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<top>0</top>
|
||||||
|
<width>880</width>
|
||||||
|
<height>69</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<textcolor>grey</textcolor>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
|
<font>font13</font>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<left>0</left>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<top>0</top>
|
||||||
|
<width>880</width>
|
||||||
|
<height>69</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<align>right</align>
|
||||||
|
<textcolor>white</textcolor>
|
||||||
|
<label>$INFO[ListItem.Label2]</label>
|
||||||
|
<font>font13</font>
|
||||||
|
</control>
|
||||||
|
</itemlayout>
|
||||||
|
<focusedlayout height="69">
|
||||||
|
<control type="image">
|
||||||
|
<left>0</left>
|
||||||
|
<top>0</top>
|
||||||
|
<width>880</width>
|
||||||
|
<height>2</height>
|
||||||
|
<texture>hathoris-list-separator.png</texture>
|
||||||
|
</control>
|
||||||
|
<control type="image">
|
||||||
|
<left>0</left>
|
||||||
|
<top>0</top>
|
||||||
|
<width>880</width>
|
||||||
|
<height>69</height>
|
||||||
|
<texture colordiffuse="FF12B2E7">hathoris-list-focus.png</texture>
|
||||||
|
<animation effect="fade" start="100" end="40" time="100" condition="ControlGroup(9001).HasFocus">Conditional</animation>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<left>0</left>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<top>0</top>
|
||||||
|
<width>880</width>
|
||||||
|
<height>69</height>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<label>$INFO[ListItem.Label]</label>
|
||||||
|
<font>font13</font>
|
||||||
|
</control>
|
||||||
|
<control type="label">
|
||||||
|
<left>0</left>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<top>0</top>
|
||||||
|
<width>880</width>
|
||||||
|
<height>69</height>
|
||||||
|
<align>right</align>
|
||||||
|
<aligny>center</aligny>
|
||||||
|
<label>$INFO[ListItem.Label2]</label>
|
||||||
|
<font>font13</font>
|
||||||
|
</control>
|
||||||
|
</focusedlayout>
|
||||||
|
</control>
|
||||||
|
<control type="grouplist" id="9001">
|
||||||
|
<left>920</left>
|
||||||
|
<top>80</top>
|
||||||
|
<onup>9001</onup>
|
||||||
|
<ondown>9001</ondown>
|
||||||
|
<onleft>10</onleft>
|
||||||
|
<onright>10</onright>
|
||||||
|
<itemgap>-20</itemgap>
|
||||||
|
<control type="button" id="18">
|
||||||
|
<width>300</width>
|
||||||
|
<height>90</height>
|
||||||
|
<label>186</label>
|
||||||
|
<font>font12_title</font>
|
||||||
|
<textcolor>white</textcolor>
|
||||||
|
<textoffsetx>20</textoffsetx>
|
||||||
|
<align>center</align>
|
||||||
|
<texturenofocus border="40">hathoris-button-nofocus.png</texturenofocus>
|
||||||
|
<texturefocus border="40" colordiffuse="FF12B2E7">hathoris-button-focus.png</texturefocus>
|
||||||
|
</control>
|
||||||
|
</control>
|
||||||
|
</controls>
|
||||||
|
</window>
|
BIN
resources/skins/default/media/hathoris-button-focus.png
Normal file
After Width: | Height: | Size: 379 B |
BIN
resources/skins/default/media/hathoris-button-nofocus.png
Normal file
After Width: | Height: | Size: 475 B |
BIN
resources/skins/default/media/hathoris-header.png
Normal file
After Width: | Height: | Size: 167 B |
BIN
resources/skins/default/media/hathoris-list-focus.png
Normal file
After Width: | Height: | Size: 383 B |
BIN
resources/skins/default/media/hathoris-list-nofocus.png
Normal file
After Width: | Height: | Size: 584 B |
BIN
resources/skins/default/media/hathoris-panel.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
resources/skins/default/media/hathoris-radio-off.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
resources/skins/default/media/hathoris-radio-on.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
resources/skins/default/media/hathoris-slider-background.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
resources/skins/default/media/hathoris-slider-nib-focus.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/skins/default/media/hathoris-slider-nib-nofocus.png
Normal file
After Width: | Height: | Size: 2.7 KiB |