From ed0c69bc9723bf1a57ae3060daf35b9e1ae29954 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 2 Jun 2024 03:30:00 +0200 Subject: [PATCH] Initial commit --- addon.py | 39 +++++ addon.xml | 14 ++ lib/main.py | 144 ++++++++++++++++++ .../resource.language.en_GB/strings.po | 110 +++++++++++++ resources/settings.xml | 5 + .../skins/default/1080i/amplifier_control.xml | 135 ++++++++++++++++ .../default/media/hathoris-button-focus.png | Bin 0 -> 379 bytes .../default/media/hathoris-button-nofocus.png | Bin 0 -> 475 bytes .../skins/default/media/hathoris-header.png | Bin 0 -> 167 bytes .../default/media/hathoris-list-focus.png | Bin 0 -> 383 bytes .../default/media/hathoris-list-nofocus.png | Bin 0 -> 584 bytes .../skins/default/media/hathoris-panel.png | Bin 0 -> 1891 bytes .../default/media/hathoris-radio-off.png | Bin 0 -> 4029 bytes .../skins/default/media/hathoris-radio-on.png | Bin 0 -> 3974 bytes .../media/hathoris-slider-background.png | Bin 0 -> 3194 bytes .../media/hathoris-slider-nib-focus.png | Bin 0 -> 2790 bytes .../media/hathoris-slider-nib-nofocus.png | Bin 0 -> 2799 bytes 17 files changed, 447 insertions(+) create mode 100644 addon.py create mode 100644 addon.xml create mode 100644 lib/main.py create mode 100644 resources/language/resource.language.en_GB/strings.po create mode 100644 resources/settings.xml create mode 100644 resources/skins/default/1080i/amplifier_control.xml create mode 100644 resources/skins/default/media/hathoris-button-focus.png create mode 100644 resources/skins/default/media/hathoris-button-nofocus.png create mode 100644 resources/skins/default/media/hathoris-header.png create mode 100644 resources/skins/default/media/hathoris-list-focus.png create mode 100644 resources/skins/default/media/hathoris-list-nofocus.png create mode 100644 resources/skins/default/media/hathoris-panel.png create mode 100644 resources/skins/default/media/hathoris-radio-off.png create mode 100644 resources/skins/default/media/hathoris-radio-on.png create mode 100644 resources/skins/default/media/hathoris-slider-background.png create mode 100644 resources/skins/default/media/hathoris-slider-nib-focus.png create mode 100644 resources/skins/default/media/hathoris-slider-nib-nofocus.png diff --git a/addon.py b/addon.py new file mode 100644 index 0000000..2415db4 --- /dev/null +++ b/addon.py @@ -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 diff --git a/addon.xml b/addon.xml new file mode 100644 index 0000000..65e313d --- /dev/null +++ b/addon.xml @@ -0,0 +1,14 @@ + + + + + + + executable + + + Control your amplifier through a REST API + Allows you to control volume, source, and other settings of your amplifier via a REST API. + all + + diff --git a/lib/main.py b/lib/main.py new file mode 100644 index 0000000..92437d7 --- /dev/null +++ b/lib/main.py @@ -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) diff --git a/resources/language/resource.language.en_GB/strings.po b/resources/language/resource.language.en_GB/strings.po new file mode 100644 index 0000000..5683e0a --- /dev/null +++ b/resources/language/resource.language.en_GB/strings.po @@ -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 "" diff --git a/resources/settings.xml b/resources/settings.xml new file mode 100644 index 0000000..3b62f24 --- /dev/null +++ b/resources/settings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/skins/default/1080i/amplifier_control.xml b/resources/skins/default/1080i/amplifier_control.xml new file mode 100644 index 0000000..b76f4b2 --- /dev/null +++ b/resources/skins/default/1080i/amplifier_control.xml @@ -0,0 +1,135 @@ + + + 10 + + 350 + 135 + + + + 0 + 0 + 1220 + 712 + hathoris-panel.png + + + 0 + 0 + 1220 + 70 + hathoris-header.png + + + 70 + -30 + 0 + 1220 + 70 + font32_title + left + center + black + + + 20 + 80 + 10 + 10 + 9001 + 9001 + -20 + 200 + + + 0 + 0 + 880 + 2 + hathoris-list-separator.png + + + 0 + 20 + 0 + 880 + 69 + center + grey + + font13 + + + 0 + 20 + 0 + 880 + 69 + center + right + white + + font13 + + + + + 0 + 0 + 880 + 2 + hathoris-list-separator.png + + + 0 + 0 + 880 + 69 + hathoris-list-focus.png + Conditional + + + 0 + 20 + 0 + 880 + 69 + center + + font13 + + + 0 + 20 + 0 + 880 + 69 + right + center + + font13 + + + + + 920 + 80 + 9001 + 9001 + 10 + 10 + -20 + + 300 + 90 + + font12_title + white + 20 + center + hathoris-button-nofocus.png + hathoris-button-focus.png + + + + diff --git a/resources/skins/default/media/hathoris-button-focus.png b/resources/skins/default/media/hathoris-button-focus.png new file mode 100644 index 0000000000000000000000000000000000000000..1bc84ff1586a758359763d6cca6d37d6a6cedbca GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^FMzn5gAGV>&g6Lqq}Y|gW!U_%O`^*3Opi< z85p<)K$vl@^4?OQAbW|YuPgflW_eLI)k#0+B{DEDa(KEphE&{odvzn{0RtXaN9zh9 z-kFLvOTs*VT``;SBCjEyLoSOKs23SL$p5u!;hCB_lV2$JA^^q;0RkEj?65!~0D>DF92Y<^V`Gy81T!(QHbOBA7ZWQ4bBM5TK`_KH rkf9VASkuEh`*P$#c2;D+fY}W~ak55i2F!ipi$Q!(S3j3^P65Hn!&&zoEPxHqc*esvm3-{6SG> zOX&Mr9+TaDt&?)UG3-;#=viRcbc{nv86V@!zjtqTr1AgV{=267rE@Y%ub_$B1qsI? n!H7KmhPCEYJ0UM32Lr$E5|+0jH``o+!OP(3>gTe~DWM4fDRhO>_%)r1c48n{Iv*t(u z1=&kHeO=k_bFwnXa0y>fiUJDpc)B=-a9mIRae$$XfuDoi(P=vF< zBeIx*fm;ZK886+f`vVkYFY)wsWq-gdFUluBN9XKqpiw-YE{-7;x87bgWMnYlIC3E9 z|EGKW8VSjJCQneExlF(*=poBFHaMZAo34##nfEtKZ3ReeYU>XTD m7-1?oO5sKV83Y`5Hn!&&zo^TlIM5#uwTM#B~7ajN3J&{ik$fWk0~udP${B?Lrk&BwPP?bnI0KmkUyvK>%bm2 zg`#K0Cy&MSO4xqiQ$BzG-e)}*tiMIRkw0#zp5}f=YT7*JZZ(qOGL7+9|p)#jZl1#Oa3yYS!UBPXojvK#? z9!=Zp_f2_LY0uSZ3WAIK6pGicQQFaQBJ|0>Jw<-?noNIJd~k7*ozyV7zY;t;KHfXN4cJC!K^?CO`Q0nz!=XwLSBHo}PF8=IdLZ r`(FRQ{_M7CT-rM8dFSn()-k;Lx@uG5E8cCuxMc8j^>bP0l+XkKGg#rY literal 0 HcmV?d00001 diff --git a/resources/skins/default/media/hathoris-panel.png b/resources/skins/default/media/hathoris-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..e8c13ebd70be0fb9b4026fa4bac916a9ffbaefc5 GIT binary patch literal 1891 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 ZP_s0VcZLa<)ve6|T59@Wgo;Dde*n0fp#J~> literal 0 HcmV?d00001 diff --git a/resources/skins/default/media/hathoris-radio-on.png b/resources/skins/default/media/hathoris-radio-on.png new file mode 100644 index 0000000000000000000000000000000000000000..8e451d5ff600f4de828344eeb94ca2b7ec7867b1 GIT binary patch literal 3974 zcmY*bbx_m|*Zt8QA}Jx=ozk#NEgg$=#}d*g-O>mwNUp4)bax|52qLg6@X#$F0s=}1 zO1(TY-}lY;{&DV^xp(Hy+%t2|++;(2O)}y~!~g)0LA2D2?>YD$I)wQ5wI>BUbx%aT zS{4BSK=SZkU;%}1=mCJ(z#R-WG<5R`^a*hD@nwa8!K}XiKCbT1T<$k46`8=GCVRAs z7i(uAouug3IzGlUgsjG()M#orH$NK@rFIfq$s&!>zyo!4T*gnuNjT~0(P=bBf}|S}$4F9aoK65hF`VZ9+S8k0Gzpgjz-a`8TMg_ zW0yHU^_CHpU%Fah+NpRMdon1C?<3dro%7dq!7Ci4bt$0HLkTMkE>vmbg!5a6htfO{ z_4NV(PI`O>{^2FWiFS+I#01?PE8l1qa{F5M}=7uW#pES?Mp5^ zXxd4x=u7d}fe<;s+Z#iKtNMxXp#$4e5|(3%IY&AnT|xXM+kh4>RVpXzSQ51nyAE7R zOUyV0%4({`e4^-&Et_bq(G8dX1c=98C-iEHy^d)%Qv8Rs4ct{J^XR30bh8*}UlRv{#{9&u!k zEjfEO6Ue?6WEJ=nzg@IFv@NmCxXpHAMO@^l;#YcU@|D;4vxhdr4&{#I4!%pixT@*< zLgUrS66gb|3`)W#lX0{VowB-jIWv#k=-e*c8@>m2KUmFS(a-y0 zHa)(l`91GD&RGyCSprSmXcCN<0YOhd4`T?TZ^+jzXP8N47=?<;h#lnb7El!Un40sq z^M}*GKNII@=WypR@{2&Ls>`d_s!2>wO~;`bRkkJ(^%|xUCRr_m@t~a zs^lvZt6`NsQ03A{i<@M8iy_@118}KPag*Iv?+7c26GKa|Ae$#yO~UgSsbTJ>$;I#& z{5|UN2_ zSbJ9I=H;5#Xw?|iXdd)Q*B@3CmG`OlSX{-BtBz{(So3JYGSGNMyC#b!s};l*w0eqi z=k>zXo@I*Jt|^80vl_27DAL?VthYF~yhgl6-gDz~vvKR_ z?du&C8tZkH^Oei%%Il6!PE5X=oGXw*D2mOC?;>^)^N95hd&^);!S>4bP8Ue?wq?1+ zQG2gFFT?;;?<2J0a@ZAc@48Ab7VlPKegObxwS&{n`d;31UVQMhEK(h&xS6P1cDW7tT`>Mlg=7e(KxS4m|g>*$kaNFG92+ z1`jKj8;+!p+>a7h-cCrq?Q)1XiNMY3 z<{LZ8f~qeZj_H(sEBU|OQrc3GSrcEgu$z!Ub$sd=HyH9k+QqMfqd4xI??UgUfQo1? zEErC5jB1P(_SI!*I9q?ZvfQ%E@8-or)wJR+RT|Zd*t*!AfjTy~Pd_s2*e-VS(Qvdo zdAwod_qi_w4&NM7hayRN;uGV|5|q<@lguAYiRDQJO|5_f*46&1<*KbAU}6Tc4MN&d z{7Tir9tuk`-ICqPBf_p06SL+;=8$JPOtQlK_0rzYKCE9Ko+f@R`bx4DMwc)0$;LON z1U+~bUqM?XOeH9ntD>8v|K#;gRZ9L!+BB|{he2HF#36&sgD`fHDe0-C1+X-dhX_j!!I)=DW-XqD5OF)G{5#Z=cb=c18RlZ<+CQ26SvI!@DKL3B z(eimYQ|8U_+q*-mL8{HH_Ob*WGa+X4Z+*2Mvds#9AkVxC}LEiNlvM!UZ%^V ziCW-Y3Ep8$^!eOhu=2XKs%E&$8RI(by7z5O2x�Tb=hUkKCJVajBJT{b}pr`ET`Q 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 literal 0 HcmV?d00001 diff --git a/resources/skins/default/media/hathoris-slider-nib-focus.png b/resources/skins/default/media/hathoris-slider-nib-focus.png new file mode 100644 index 0000000000000000000000000000000000000000..0bd8bab4b5b391b97fdaaa4c89eafc93c8ee727d GIT binary patch literal 2790 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 literal 0 HcmV?d00001 diff --git a/resources/skins/default/media/hathoris-slider-nib-nofocus.png b/resources/skins/default/media/hathoris-slider-nib-nofocus.png new file mode 100644 index 0000000000000000000000000000000000000000..aeb4597a490afba86d88d92ff6b2fb383f83f6ee GIT binary patch literal 2799 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 BGgtrs literal 0 HcmV?d00001