Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion regarding the Handy and somewhat about connecting to devices via Wi-Fi/Web API's #606

Open
Gremious opened this issue Jan 17, 2024 · 9 comments
Assignees
Labels
Features New things to add to the project

Comments

@Gremious
Copy link

Gremious commented Jan 17, 2024

I raise this discussion from having spent an evening reading the implementation for the Handy, and it's bluetooth interface handyplug. At first, I thought "haha @qdot left angry comments, that's funny to read". As I spent time familiarizing myself with the code, that feeling has since grown into a deep understanding. I am so sorry.

The handy is a stroker. The handyplug protocol is a little chunk of code (?) that hasn't been touched in two years, and implements a single useful control command: move to point X and take Y ms doing it. A linear movement, not an oscillation, for an oscillating toy.

The dev team for the handy does not care for developing this further:

We have limited developer resources, and our main focus is on our online platform. If you want to create a project using BLE, we recommend using buttplug.io.
-- the handy team

Through great difficulty, handyplug was used and buttplug now supports a linear movement for the handy.

Intiface has a button to oscillate said linear movement so that's cool.

The handy, however, remains an oscillating piece of hardware.


This sucks lmao. I want to hack on existing code and update it to oscillate the handy, but I have to write manual back and forth motions with timings and sleeps between them, instead of ideally just translating a vibration call to an oscillation call.

On the other hand, the web API for this toy looks actually reasonable, as it implements alternating motions - start, stop, intensity, etc.

My questions then are the following:

Is connecting over Wi-Fi/implementing web API's for toys in scope for this project? Would it be reasonable to open a request for that? If it happens I probably wouldn't mind doing a PR the Handy API, at the very least for the alternating motions part.

Alternatively idk, can I communicate with intiface somehow and tell it to do whatever it does when I press the oscillation button?

Is there a magical third way?

Maybe I can write some sort of compatibility layer that handles looping movement, that also connects to intiface somehow someway...


Basically, it should not be my job to manually oscillate a toy, I should just tell buttplug to do it. It should not be buttplug's job to do it manually ether, it should just relay to the toy to do it. The toys suck and doesn't do it - so I'm just kinda looking for alternate solutions here.

@Gremious Gremious added the Features New things to add to the project label Jan 17, 2024
@qdot
Copy link
Member

qdot commented Jan 17, 2024

Yeah, welcome to the fun of trying to maintain a generic API for a set of devices that are in no way generic.

So, first off: Hadn't seen Handy's new API. Main issue I see here (and I'm making big assumptions about architecture) is that it still requires talking to their server for every command, so you're network round-tripping off their endpoints, versus local control. That was mainly what we were trying to avoid when we said no wifi/web APIs in Buttplug: I don't want to debug everyone's individual network latency in a platform that should really not require it in the first place, but we're forced into these weird control models and local network discovery is the w o r s t (I just had a bad time with mDNS last month).

Now, that said, if you want a build a system where you take a vibration and turn that into an oscillation through Handy's Wifi system, that's completely possible through our poorly documented "Websocket Device Manager". Turning this on pops open yet another websocket server port that you can connect to and receive buttplug commands either as the raw JSON that they come in as, or formatted to the values of any other protocol our system handles. It does require setup via it's own simple protocol, and setting up the configuration part is still hellish (I'm working on integrating configuration of this into Intiface now, but it's requiring a ton of ground-up reworking), but once all the setup is done, it's pretty simple. There's documentation on this at https://docs.buttplug.io/docs/dev-guide/inflating-buttplug/devices/websocket-device-manager, or just poke me on here/discord/etc if you have other questions.

@Gremious
Copy link
Author

That was mainly what we were trying to avoid when we said no wifi/web APIs in Buttplug ...

100% understandandable


"Websocket Device Manager" looks, at least in principle, like the right solution so far - thank you very much!

I think it wouldn't be far fetched to write some software that actually handles oscilation and ether translates vibrations or just immidates a handy more properly. I can probably still directly communicate with a BLE connected handy and do looping movements manually too, so I don't need to ask for wifi keys everywhere.

Thanks again, I'll look into it more properly and pop around if I have any questions!

@Gremious
Copy link
Author

Gremious commented Jan 17, 2024

Heya! I set it up and it was honestly not that bad! And hey intiface seems to have GUI buttons to add user configs for websocket devices now, so that's nice.

My thoughts are that I can emulate a vibrating device/accept vibration messages, since most applications expect that, and simply route those to a connected handy appropriately.

However, it's a lil lame to show up as a "lovense hush" or something - I was wondering if buttplug has a "Generic/Software Device" that supports all types of signals? Or perhaps I can extend the handy user config with a linear message somehow?

I tried some random funky things e.g. :

Spoiler warning
{
   "version": {
   	"major": 2,
   	"minor": 6
   },
   "user-configs": {
   	"specifiers": {
   		"thehandy": {
   			"websocket": {
   				"names": [
   					"HandyCoordinator"
   				]
   			},
   			"configurations": [
   				{
   					"name": "The Handy Coordinator",
   					"messages": {
   						"LinearCmd": [
   							{
   								"StepRange": [
   									0,
   									100
   								],
   								"ActuatorType": "Position"
   							}
   						],
   						"ScalarCmd": [
   							{
   								"StepRange": [
   									0,
   									100
   								],
   								"ActuatorType": "Oscillate",
   								"FeatureDescriptor": "Handy Oscillation Speed"
   							}
   						]
   					}
   				}
   			]
   		}
   	}
   }
}

But that didn't really change anything, no nice oscillation/vibration buttons in intiface.

@qdot
Copy link
Member

qdot commented Jan 17, 2024

Ok, so this is going to delve a bit into how protocols and configurations work internally.

You do not want to use thehandy as your basis. That's referring to OUR implementation of the Handy Protocol, which means a buttplug message gets turned into Handy's protocol format, THEN gets sent to you. That conversion from buttplug -> proprietary protocol is lossy; if a toy using that protocol does not provide a feature type, we don't implement it. The Handy (at the moment, disregarding CES news) does not currently have a vibrator, so we don't implement there, meaning even if you add new configurations, we'll just toss it because you're routing through our implementation of the protocol.

On top of that, setting new configuration on The Handy would require an identification step that does not exist on that protocol. Identification steps happen on some brands like Lovense or Satisfyer, where we have to use some extra data like response to a certain command or advertising manufacturer data to figure out what kind of toy under that brand we're talking to and specialize for it. For The Handy, there's just... The Handy. So we didn't create identification logic and I don't think there's the ability to do that otherwise.

What you probably want to do is choose either the "buttplug-passthru" protocol and parse raw buttplug messages yourself, or choose the protocol of something that already supports vibration, make a separate configuration, take the output of that, and translate the vibration/output to the corresponding handy web commands.

@Gremious
Copy link
Author

Gremious commented Jan 17, 2024

Yep, that all makes perfect sense from what what I've seen working with this.

buttplug-passthru is what I am looking for - thank you very much! My only reference was the buttplug-device-config.json and passthru was not there, so I didn't see it.

Though, using it and sending a handshake immediately gives me a Close message and then a 10053: ConnectionAborted - unlike when e.g emulating a lovense device. I can't find it in the docs so I'd like to ask how this thing behaves differently from a regular device, please, do I need to configure/do anything special?

Spoiler log
55.969 : [I] : Got connection
55.969 : [D] : Server handshake done.
55.969 : [I] : Device Websocket Device HandyCoordinator (6B201307C45C) found.
55.969 : [D] : Device 6B201307C45C allowed via configuration file, continuing.
55.969 : [I] : Starting websocket server connection event loop.
55.969 : [D] : Looking for protocol that matches specifier: Websocket(WebsocketSpecifier { names: {"HandyCoordinator"} })
55.969 : [D] : No viable protocols for hardware Websocket(WebsocketSpecifier { names: {"HandyCoordinator"} }), ignoring.
55.969 : [I] : Websocket server connector owner dropped, disconnecting websocket connection.
55.969 : [D] : Exiting Websocket Server Device control loop.

@qdot
Copy link
Member

qdot commented Jan 17, 2024

Ok, so the important error message here is:

No viable protocols for hardware Websocket(WebsocketSpecifier { names: {"HandyCoordinator"} }), ignoring.

That means the system isn't matching your specifier to a protocol. Can you post what your current config file looks like?

(I am currently replacing this device config mess with a sqlite based DB mess, so this is a temporary thing)

@Gremious
Copy link
Author

Knew I should have included that, here you go:

{
  "version": {
    "major": 2,
    "minor": 0
  },
  "user-configs": {
    "specifiers": {
      "buttplug-passthru": {
        "websocket": {
          "names": [
            "HandyCoordinator"
          ]
        },
        "configurations": []
      }
    },
    "devices": [
      {
        "identifier": {
          "address": "PeripheralId(80:64:6F:27:B0:C6)",
          "protocol": "thehandy",
          "identifier": "The Handy"
        },
        "config": {
          "index": 0
        }
      }
    ]
  }
}

@qdot
Copy link
Member

qdot commented Jan 17, 2024

Ok, I think you need to set up a default in the buttplug-passthru specifier, where configurations currently is. That's how we say "if I can't identify a device, just use this". Just copy something simple out of the main device config, that has a vibrator with 100 steps or something. I usually use the aneros one, it's fairly simple.

@Gremious
Copy link
Author

Gremious commented Jan 17, 2024

I tried both

"buttplug-passthru": {
	"websocket": {
		"names": [
			"HandyCoordinator"
		]
	},
	"defaults": {
		"name": "HandyCoordinator",
		"messages": {
			"ScalarCmd": [
				{
					"StepRange": [
						0,
						127
					],
					"FeatureDescriptor": "Perineum Vibrator",
					"ActuatorType": "Vibrate"
				},
				{
					"StepRange": [
						0,
						127
					],
					"FeatureDescriptor": "Internal Vibrator",
					"ActuatorType": "Vibrate"
				}
			]
		}
	}
}

and

"buttplug-passthru": {
	"websocket": {
		"names": [
			"HandyCoordinator"
		]
	},
	"configurations": [
		{
			"defaults": {
				"name": "HandyCoordinator",
				"messages": {
					"ScalarCmd": [
						{
							"StepRange": [
								0,
								127
							],
							"FeatureDescriptor": "Perineum Vibrator",
							"ActuatorType": "Vibrate"
						},
						{
							"StepRange": [
								0,
								127
							],
							"FeatureDescriptor": "Internal Vibrator",
							"ActuatorType": "Vibrate"
						}
					]
				}
			}
		}
	]
}

and it did not lead to any changes, unless I misunderstood the assignment.

p.s. idk if u prefer discord so i joined just in case, u can ping me if you want, same name. I don't mind ether, though I will be dropping out in like 30 min tops for the night

sorry to put you on debug duty, I appreciate the help <3

update: yep it broken just emulate a real toy for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Features New things to add to the project
Projects
None yet
Development

No branches or pull requests

2 participants