Add PushToTalk Spoon to Hammerspoon config

This commit is contained in:
2020-05-06 20:39:48 +01:00
parent e458b4cb19
commit 55f96f74ca
8 changed files with 390 additions and 0 deletions

View File

@@ -70,3 +70,4 @@ endef
$(eval $(call dep-file,inspect.lua,"https://github.com/kikito/inspect.lua/raw/v3.1.0/inspect.lua"))
$(eval $(call dep-spoon,RoundedCorners,"https://github.com/Hammerspoon/Spoons/raw/master/Spoons/RoundedCorners.spoon.zip"))
$(eval $(call dep-spoon,HeadphoneAutoPause,"https://github.com/Hammerspoon/Spoons/raw/master/Spoons/HeadphoneAutoPause.spoon.zip"))
$(eval $(call dep-spoon,PushToTalk,"https://github.com/Hammerspoon/Spoons/raw/master/Spoons/PushToTalk.spoon.zip"))

View File

@@ -0,0 +1,100 @@
[
{
"Command": [],
"Constant": [],
"Constructor": [],
"Deprecated": [],
"Field": [],
"Function": [],
"Method": [
{
"def": "PushToTalk:init()",
"desc": "Starts menu and key watcher",
"doc": "Starts menu and key watcher",
"name": "init",
"signature": "PushToTalk:init()",
"stripped_doc": "",
"type": "Method"
},
{
"def": "PushToTalk:stop()",
"desc": "Stops PushToTalk",
"doc": "Stops PushToTalk",
"name": "stop",
"signature": "PushToTalk:stop()",
"stripped_doc": "",
"type": "Method"
},
{
"def": "PushToTalk:toggleStates()",
"desc": "Cycle states in order",
"doc": "Cycle states in order\n\nParameters:\n * states - A array of states to toggle. For example: `{'push-to-talk', 'release-to-talk'}`",
"name": "toggleStates",
"parameters": [
" * states - A array of states to toggle. For example: `{'push-to-talk', 'release-to-talk'}`"
],
"signature": "PushToTalk:toggleStates()",
"stripped_doc": "",
"type": "Method"
}
],
"Variable": [
{
"def": "PushToTalk.app_switcher",
"desc": "Takes mapping from application name to mic state.",
"doc": "Takes mapping from application name to mic state.\nFor example this `{ ['zoom.us'] = 'push-to-talk' }` will switch mic to `push-to-talk` state when Zoom app starts.",
"name": "app_switcher",
"signature": "PushToTalk.app_switcher",
"stripped_doc": "For example this `{ ['zoom.us'] = 'push-to-talk' }` will switch mic to `push-to-talk` state when Zoom app starts.",
"type": "Variable"
}
],
"desc": "Implements push-to-talk and push-to-mute functionality with `fn` key.",
"doc": "Implements push-to-talk and push-to-mute functionality with `fn` key.\nI implemented this after reading Gitlab remote handbook https://about.gitlab.com/handbook/communication/ about Shush utility.\n\nMy workflow:\n\nWhen Zoom starts, PushToTalk automatically changes mic state from `default`\nto `push-to-talk`, so I need to press `fn` key to unmute myself and speak.\nIf I need to actively chat in group meeting or it's one-on-one meeting,\nI'm switching to `push-to-mute` state, so mic will be unmute by default and `fn` key mutes it.\n\nPushToTalk has menubar with colorful icons so you can easily see current mic state.\n\nSample config: `spoon.SpoonInstall:andUse(\"PushToTalk\", {start = true, config = { app_switcher = { ['zoom.us'] = 'push-to-talk' }}})`\nand separate keybinding to toggle states with lambda function `function() spoon.PushToTalk.toggleStates({'push-to-talk', 'release-to-talk'}) end`\n\nCheck out my config: https://github.com/skrypka/hammerspoon_config/blob/master/init.lua",
"items": [
{
"def": "PushToTalk.app_switcher",
"desc": "Takes mapping from application name to mic state.",
"doc": "Takes mapping from application name to mic state.\nFor example this `{ ['zoom.us'] = 'push-to-talk' }` will switch mic to `push-to-talk` state when Zoom app starts.",
"name": "app_switcher",
"signature": "PushToTalk.app_switcher",
"stripped_doc": "For example this `{ ['zoom.us'] = 'push-to-talk' }` will switch mic to `push-to-talk` state when Zoom app starts.",
"type": "Variable"
},
{
"def": "PushToTalk:init()",
"desc": "Starts menu and key watcher",
"doc": "Starts menu and key watcher",
"name": "init",
"signature": "PushToTalk:init()",
"stripped_doc": "",
"type": "Method"
},
{
"def": "PushToTalk:stop()",
"desc": "Stops PushToTalk",
"doc": "Stops PushToTalk",
"name": "stop",
"signature": "PushToTalk:stop()",
"stripped_doc": "",
"type": "Method"
},
{
"def": "PushToTalk:toggleStates()",
"desc": "Cycle states in order",
"doc": "Cycle states in order\n\nParameters:\n * states - A array of states to toggle. For example: `{'push-to-talk', 'release-to-talk'}`",
"name": "toggleStates",
"parameters": [
" * states - A array of states to toggle. For example: `{'push-to-talk', 'release-to-talk'}`"
],
"signature": "PushToTalk:toggleStates()",
"stripped_doc": "",
"type": "Method"
}
],
"name": "PushToTalk",
"stripped_doc": "I implemented this after reading Gitlab remote handbook https://about.gitlab.com/handbook/communication/ about Shush utility.\n\nMy workflow:\n\nWhen Zoom starts, PushToTalk automatically changes mic state from `default`\nto `push-to-talk`, so I need to press `fn` key to unmute myself and speak.\nIf I need to actively chat in group meeting or it's one-on-one meeting,\nI'm switching to `push-to-mute` state, so mic will be unmute by default and `fn` key mutes it.\n\nPushToTalk has menubar with colorful icons so you can easily see current mic state.\n\nSample config: `spoon.SpoonInstall:andUse(\"PushToTalk\", {start = true, config = { app_switcher = { ['zoom.us'] = 'push-to-talk' }}})`\nand separate keybinding to toggle states with lambda function `function() spoon.PushToTalk.toggleStates({'push-to-talk', 'release-to-talk'}) end`\n\nCheck out my config: https://github.com/skrypka/hammerspoon_config/blob/master/init.lua",
"submodules": [],
"type": "Module"
}
]

View File

@@ -0,0 +1,147 @@
--- === PushToTalk ===
---
--- Implements push-to-talk and push-to-mute functionality with `fn` key.
--- I implemented this after reading Gitlab remote handbook https://about.gitlab.com/handbook/communication/ about Shush utility.
---
--- My workflow:
---
--- When Zoom starts, PushToTalk automatically changes mic state from `default`
--- to `push-to-talk`, so I need to press `fn` key to unmute myself and speak.
--- If I need to actively chat in group meeting or it's one-on-one meeting,
--- I'm switching to `push-to-mute` state, so mic will be unmute by default and `fn` key mutes it.
---
--- PushToTalk has menubar with colorful icons so you can easily see current mic state.
---
--- Sample config: `spoon.SpoonInstall:andUse("PushToTalk", {start = true, config = { app_switcher = { ['zoom.us'] = 'push-to-talk' }}})`
--- and separate keybinding to toggle states with lambda function `function() spoon.PushToTalk.toggleStates({'push-to-talk', 'release-to-talk'}) end`
---
--- Check out my config: https://github.com/skrypka/hammerspoon_config/blob/master/init.lua
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "PushToTalk"
obj.version = "0.1"
obj.author = "Roman Khomenko <roman.dowakin@gmail.com>"
obj.homepage = "https://github.com/Hammerspoon/Spoons"
obj.license = "MIT - https://opensource.org/licenses/MIT"
obj.defaultState = 'unmute'
obj.state = obj.defaultState
obj.pushed = false
--- PushToTalk.app_switcher
--- Variable
--- Takes mapping from application name to mic state.
--- For example this `{ ['zoom.us'] = 'push-to-talk' }` will switch mic to `push-to-talk` state when Zoom app starts.
obj.app_switcher = {}
local function showState()
local device = hs.audiodevice.defaultInputDevice()
local muted = false
if obj.state == 'unmute' then
obj.menubar:setIcon(hs.spoons.resourcePath("speak.pdf"))
elseif obj.state == 'mute' then
obj.menubar:setIcon(hs.spoons.resourcePath("muted.pdf"))
muted = true
elseif obj.state == 'push-to-talk' then
if obj.pushed then
obj.menubar:setIcon(hs.spoons.resourcePath("record.pdf"), false)
else
obj.menubar:setIcon(hs.spoons.resourcePath("unrecord.pdf"))
muted = true
end
elseif obj.state == 'release-to-talk' then
if obj.pushed then
obj.menubar:setIcon(hs.spoons.resourcePath("unrecord.pdf"))
muted = true
else
obj.menubar:setIcon(hs.spoons.resourcePath("record.pdf"), false)
end
end
device:setMuted(muted)
end
function obj.setState(s)
obj.state = s
showState()
end
obj.menutable = {
{ title = "UnMuted", fn = function() obj.setState('unmute') end },
{ title = "Muted", fn = function() obj.setState('mute') end },
{ title = "Push-to-talk (fn)", fn = function() obj.setState('push-to-talk') end },
{ title = "Release-to-talk (fn)", fn = function() obj.setState('release-to-talk') end },
}
local function appWatcher(appName, eventType, appObject)
local new_app_state = obj.app_switcher[appName];
if (new_app_state) then
if (eventType == hs.application.watcher.launching) then
obj.setState(new_app_state)
elseif (eventType == hs.application.watcher.terminated) then
obj.setState(obj.defaultState)
end
end
end
local function eventTapWatcher(event)
device = hs.audiodevice.defaultInputDevice()
if event:getFlags()['fn'] then
obj.pushed = true
else
obj.pushed = false
end
showState()
end
--- PushToTalk:init()
--- Method
--- Initial setup. It's empty currently
function obj:init()
end
--- PushToTalk:init()
--- Method
--- Starts menu and key watcher
function obj:start()
self:stop()
obj.appWatcher = hs.application.watcher.new(appWatcher)
obj.appWatcher:start()
obj.eventTapWatcher = hs.eventtap.new({hs.eventtap.event.types.flagsChanged}, eventTapWatcher)
obj.eventTapWatcher:start()
obj.menubar = hs.menubar.new()
obj.menubar:setMenu(obj.menutable)
obj.setState(obj.state)
end
--- PushToTalk:stop()
--- Method
--- Stops PushToTalk
function obj:stop()
if obj.appWatcher then obj.appWatcher:stop() end
if obj.eventTapWatcher then obj.eventTapWatcher:stop() end
if obj.menubar then obj.menubar:delete() end
end
--- PushToTalk:toggleStates()
--- Method
--- Cycle states in order
---
--- Parameters:
--- * states - A array of states to toggle. For example: `{'push-to-talk', 'release-to-talk'}`
function obj:toggleStates(states)
new_state = states[1]
for i, v in pairs(states) do
if v == obj.state then
new_state = states[(i % #states) + 1]
end
end
obj.setState(new_state)
end
return obj

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,68 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD>
3 0 obj
<< /Length 4 0 R
/Filter /FlateDecode
>>
stream
x<EFBFBD>]<5D>In<49>0 E<><<3C>N<EFBFBD><4E><EFBFBD><EFBFBD><EFBFBD>=Ba <20>"]<5D><>?P<>$<24><>0 <20>gq<67><71><EFBFBD>9<><39><EFBFBD><EFBFBD>><3E>r<EFBFBD><72>e<>VS<56>>(}'ǎ4j<34><6A>*<2A>\<5C>v0<76><17><>Tz<54><7A>,+t شz<D8B4>((<28>yZ@<40><><11><>m<EFBFBD><17>f<EFBFBD><66>t<EFBFBD>ex<65><78>E<EFBFBD><45><EFBFBD><EFBFBD><12>'C<>k9o<39><6F><EFBFBD><EFBFBD>:<3A><03><>6<EFBFBD><36><EFBFBD><EFBFBD> <0C>8s;<3B>ҩ <0B><>H<11>kדO<D793>uw<75>C+<2B>FT<46>{\CIeV<65>6x<36><78>zsm،@<40><><<3C><>j<EFBFBD>̭<EFBFBD><CCAD><EFBFBD>a<EFBFBD>(<28>L[<5B>jx<6A><78><EFBFBD><04><><EFBFBD><EFBFBD>Ow<4F><77>yi<79><69>QA<51><41>5<EFBFBD><35>bZ<62>
<EFBFBD><EFBFBD>q=FO<46><4F><EFBFBD><EFBFBD>F<EFBFBD>&}<7D><><1D>*<2A>;z<0F><>;<3B><><EFBFBD><1A><><1B><><EFBFBD>OAq<41>˔f9<66>b<EFBFBD><62><EFBFBD>p<>O<EFBFBD>nv<6E><76>
endstream
endobj
4 0 obj
310
endobj
2 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
5 0 obj
<< /Type /Page
/Parent 1 0 R
/MediaBox [ 0 0 15.75 15.75 ]
/Contents 3 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 2 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 5 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Creator (cairo 1.14.10 (http://cairographics.org))
/Producer (cairo 1.14.10 (http://cairographics.org))
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000714 00000 n
0000000424 00000 n
0000000015 00000 n
0000000402 00000 n
0000000496 00000 n
0000000779 00000 n
0000000908 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
960

View File

@@ -0,0 +1,69 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD>
3 0 obj
<< /Length 4 0 R
/Filter /FlateDecode
>>
stream
x<EFBFBD>]<5D>=
A <0C><><EFBFBD><EFBFBD>]<5D><>Lv<4C>{A<>X-<2D>BFP<46>- <0B><><EFBFBD>m<EFBFBD> C<><43><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ۛ<><DB9B>&a<><61><EFBFBD><EFBFBD>!<21>]<5D><16>i<EFBFBD> <0B><>7<EFBFBD>h,<2C>4<EFBFBD><06>qg<>3:<3A>pp+<2B>B<EFBFBD><42>P$<24><> <09><><EFBFBD><EFBFBD><EFBFBD>jN<6A><4E>Rߘ:E֬k&c<><63><EFBFBD>km<6B><6D><1A><08>-?<3F><12><><EFBFBD>Ϙh<CF98>/C3b
endstream
endobj
4 0 obj
149
endobj
2 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
5 0 obj
<< /Type /Page
/Parent 1 0 R
/MediaBox [ 0 0 15.75 15.75 ]
/Contents 3 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 2 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 5 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Creator (cairo 1.14.10 (http://cairographics.org))
/Producer (cairo 1.14.10 (http://cairographics.org))
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000553 00000 n
0000000263 00000 n
0000000015 00000 n
0000000241 00000 n
0000000335 00000 n
0000000618 00000 n
0000000747 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
799
%%EOF

View File

@@ -28,6 +28,11 @@ hs.loadSpoon('HeadphoneAutoPause')
spoon.HeadphoneAutoPause.autoResume = false
spoon.HeadphoneAutoPause:start()
-- Enable push-to-talk microphone functionality when specific apps are running.
hs.loadSpoon('PushToTalk')
spoon.PushToTalk.app_switcher = { ['TeamSpeak 3'] = 'push-to-talk' }
spoon.PushToTalk:start()
--------------------------------------------------------------------------------
-- Host specific configuration
--------------------------------------------------------------------------------