Merge standalone neato botvac plugin
parent
68ca8605f3
commit
115ce5168a
|
|
@ -480,6 +480,21 @@ Description: nymea.io plugin for a generic MQTT client
|
||||||
This package will install a generic MQTT client plugin for nymea.io
|
This package will install a generic MQTT client plugin for nymea.io
|
||||||
|
|
||||||
|
|
||||||
|
Package: nymea-plugin-neatobotvac
|
||||||
|
Architecture: any
|
||||||
|
Depends: ${shlibs:Depends},
|
||||||
|
${misc:Depends},
|
||||||
|
nymea-plugins-translations,
|
||||||
|
Description: nymea.io plugin for neato
|
||||||
|
The nymea daemon is a plugin based IoT (Internet of Things) server. The
|
||||||
|
server works like a translator for devices, things and services and
|
||||||
|
allows them to interact.
|
||||||
|
With the powerful rule engine you are able to connect any device available
|
||||||
|
in the system and create individual scenes and behaviors for your environment.
|
||||||
|
.
|
||||||
|
This package will install the nymea.io plugin for Neato Botvac robots.
|
||||||
|
|
||||||
|
|
||||||
Package: nymea-plugin-netatmo
|
Package: nymea-plugin-netatmo
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: ${shlibs:Depends},
|
Depends: ${shlibs:Depends},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
neatobotvac/integrationpluginneatobotvac.json usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/neatobotvac/
|
||||||
|
neatobotvac/integrationpluginneatobotvac.py usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/neatobotvac/
|
||||||
|
neatobotvac/requirements.txt usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/neatobotvac/
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
{
|
||||||
|
"name": "neato",
|
||||||
|
"displayName": "Neato",
|
||||||
|
"id": "4f6ecb6f-a7fe-4fdb-b8d8-45b1f235110c",
|
||||||
|
"apiKeys": ["neato"],
|
||||||
|
"vendors": [
|
||||||
|
{
|
||||||
|
"name": "neato",
|
||||||
|
"displayName": "Neato Robotics",
|
||||||
|
"id": "d2a234a5-0aeb-4c04-98d5-6428cd266433",
|
||||||
|
"thingClasses": [
|
||||||
|
{
|
||||||
|
"id": "fe594fb0-b712-4f23-8267-649eb459747b",
|
||||||
|
"name": "account",
|
||||||
|
"displayName": "Neato account",
|
||||||
|
"createMethods": ["User"],
|
||||||
|
"interfaces": ["account"],
|
||||||
|
"setupMethod": "oauth",
|
||||||
|
"stateTypes":[
|
||||||
|
{
|
||||||
|
"id": "e8f47781-e3fd-416f-a9ac-51ef942d0573",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"displayNameEvent": "Connected/disconnected",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b0db7079-49f0-444a-9c55-4bb4c764f3cb",
|
||||||
|
"name": "loggedIn",
|
||||||
|
"displayName": "Logged in",
|
||||||
|
"displayNameEvent": "Logged in or out",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actionTypes": [
|
||||||
|
{
|
||||||
|
"id": "a4b5f07f-e71a-4c3a-8d6b-50162a455159",
|
||||||
|
"name": "getMaps",
|
||||||
|
"displayName": "Get available maps"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b924c87a-f783-4f45-a3af-929684c24aea",
|
||||||
|
"name": "robot",
|
||||||
|
"displayName": "Neato robot",
|
||||||
|
"createMethods": ["auto"],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "def9a4bb-7a7e-4e3a-a63c-c55a105abb5e",
|
||||||
|
"name": "serial",
|
||||||
|
"displayName": "Robot Serial",
|
||||||
|
"type": "QString"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3793e48b-043e-43cb-b672-7c1e2e90bc8e",
|
||||||
|
"name": "secret",
|
||||||
|
"displayName": "Secret",
|
||||||
|
"type": "QString"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "141f0d98-1806-432c-aaac-c0d3a89a8e58",
|
||||||
|
"name": "mapId",
|
||||||
|
"displayName": "Map ID",
|
||||||
|
"type": "QString"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interfaces":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"settingsTypes": [
|
||||||
|
{
|
||||||
|
"id": "dabaafd3-908f-4f06-8039-5a7a729346da",
|
||||||
|
"name": "eco",
|
||||||
|
"displayName": "Eco",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "86694abb-5633-4e62-bd6c-325eb246c683",
|
||||||
|
"name": "care",
|
||||||
|
"displayName": "Extra Care",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f72bcfbd-a262-44b3-ad75-9bb094aa2bb1",
|
||||||
|
"name": "noGoLines",
|
||||||
|
"displayName": "No-go Lines Enabled",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes":[
|
||||||
|
{
|
||||||
|
"id": "dce4f7f3-a0a6-46bb-9216-c9089d9e9b0d",
|
||||||
|
"name": "cleaning",
|
||||||
|
"displayName": "Cleaning",
|
||||||
|
"displayNameEvent": "Cleaning yes/no",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0f925abf-396c-437e-b259-2fed7eafe7f4",
|
||||||
|
"name": "paused",
|
||||||
|
"displayName": "Paused",
|
||||||
|
"displayNameEvent": "Cleaning paused yes/no",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1b8abd35-8276-44ba-8c75-a647877b2e11",
|
||||||
|
"name": "charging",
|
||||||
|
"displayName": "Charging",
|
||||||
|
"displayNameEvent": "Robot charging yes/no",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": true,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "805175ec-c2e4-4fbe-9505-282750ef1467",
|
||||||
|
"name": "docked",
|
||||||
|
"displayName": "Docked",
|
||||||
|
"displayNameEvent": "Robot docked yes/no",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": true,
|
||||||
|
"cached": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "20ed8767-806f-4ec2-8626-842cd398f9df",
|
||||||
|
"name": "batteryLevel",
|
||||||
|
"displayName": "Battery level",
|
||||||
|
"displayNameEvent": "Battery level percentage",
|
||||||
|
"type": "int",
|
||||||
|
"defaultValue": 0,
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"cached": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actionTypes": [
|
||||||
|
{
|
||||||
|
"id": "1f774998-5fa7-4e3b-8ab0-a8402dd561bb",
|
||||||
|
"name": "startCleaning",
|
||||||
|
"displayName": "Start/pause cleaning"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5178a803-5696-4ee1-80a4-2c7c20a5043a",
|
||||||
|
"name": "goToBase",
|
||||||
|
"displayName": "Go to base"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "30775042-55a7-4f1b-9042-a9bdeadc4b0d",
|
||||||
|
"name": "stopCleaning",
|
||||||
|
"displayName": "Stop cleaning"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "95ba515b-0023-4a98-a867-ca8286512a4e",
|
||||||
|
"name": "getBoundaries",
|
||||||
|
"displayName": "Get No-go Lines"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
import nymea
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from pybotvac import Account, Neato, OAuthSession, PasswordlessSession, PasswordSession, Vorwerk, Robot
|
||||||
|
import json
|
||||||
|
|
||||||
|
# pybotvac library: https://github.com/stianaske/pybotvac
|
||||||
|
|
||||||
|
thingsAndRobots = {}
|
||||||
|
oauthSessions = {}
|
||||||
|
|
||||||
|
pollTimer = None
|
||||||
|
|
||||||
|
def startPairing(info):
|
||||||
|
# Start OAuth2 session
|
||||||
|
apiKey = apiKeyStorage().requestKey("neato")
|
||||||
|
oauthSession = OAuthSession(client_id=apiKey.data("clientId"), client_secret=apiKey.data("clientSecret"), redirect_uri="https://127.0.0.1:8888", vendor=Neato())
|
||||||
|
oauthSessions[info.transactionId] = oauthSession;
|
||||||
|
authorizationUrl = oauthSession.get_authorization_url()
|
||||||
|
info.oAuthUrl = authorizationUrl
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
|
||||||
|
def confirmPairing(info, username, secret):
|
||||||
|
# The user has successfully logged in at neato. Obtain the token from the OAuth session
|
||||||
|
token = oauthSessions[info.transactionId].fetch_token(secret)
|
||||||
|
pluginStorage().beginGroup(info.thingId)
|
||||||
|
pluginStorage().setValue("token", json.dumps(token))
|
||||||
|
pluginStorage().endGroup();
|
||||||
|
del oauthSessions[info.transactionId]
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
def setupThing(info):
|
||||||
|
# Setup for the account
|
||||||
|
if info.thing.thingClassId == accountThingClassId:
|
||||||
|
pluginStorage().beginGroup(info.thing.id)
|
||||||
|
token = json.loads(pluginStorage().value("token"))
|
||||||
|
logger.log("setup", token)
|
||||||
|
pluginStorage().endGroup();
|
||||||
|
|
||||||
|
try:
|
||||||
|
oAuthSession = OAuthSession(token=token)
|
||||||
|
# Login went well, finish the setup
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
except:
|
||||||
|
# Login error
|
||||||
|
info.finish(nymea.ThingErrorAuthenticationFailure, "Login error")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Mark the account as logged-in and connected
|
||||||
|
info.thing.setStateValue(accountLoggedInStateTypeId, True)
|
||||||
|
info.thing.setStateValue(accountConnectedStateTypeId, True)
|
||||||
|
|
||||||
|
# Create an account session on the session to get info about the login
|
||||||
|
account = Account(oAuthSession)
|
||||||
|
|
||||||
|
# List all robots associated with account
|
||||||
|
logger.log("account created. Robots:", account.robots);
|
||||||
|
|
||||||
|
logger.log("Persistent maps: ", account.persistent_maps)
|
||||||
|
mapDict = account.persistent_maps
|
||||||
|
mapKeys = mapDict.keys()
|
||||||
|
logger.log("Keys: ", mapKeys)
|
||||||
|
mapValues = mapDict.values()
|
||||||
|
logger.log("Values: ", mapValues)
|
||||||
|
|
||||||
|
thingDescriptors = []
|
||||||
|
for robot in account.robots:
|
||||||
|
logger.log(robot)
|
||||||
|
# Check if this robot is already added in nymea
|
||||||
|
found = False
|
||||||
|
for thing in myThings():
|
||||||
|
if thing.paramValue(robotThingSerialParamTypeId) == robot.serial:
|
||||||
|
# Yep, already here... skip it
|
||||||
|
found = True
|
||||||
|
continue
|
||||||
|
if found:
|
||||||
|
continue
|
||||||
|
|
||||||
|
thingDescriptor = nymea.ThingDescriptor(robotThingClassId, robot.name)
|
||||||
|
logger.log("MapID for Serial: ", robot.serial, mapDict[robot.serial])
|
||||||
|
mapInfo = mapDict[robot.serial]
|
||||||
|
logger.log("Type mapInfo: ", type(mapInfo))
|
||||||
|
logger.log("Contents mapInfo: ", mapInfo)
|
||||||
|
mapInfo2 = mapInfo[0]
|
||||||
|
logger.log("MapInfo2 type: ", type(mapInfo2), " MapInfo2 contents: ", mapInfo2)
|
||||||
|
mapId = mapInfo2['id']
|
||||||
|
logger.log("MapId type: ", type(mapId), " MapId contents: ", mapId)
|
||||||
|
mapName = mapInfo2['name']
|
||||||
|
logger.log("MapName type: ", type(mapName), " MapName contents: ", mapName)
|
||||||
|
mapIDshort = mapId
|
||||||
|
thingDescriptor.params = [
|
||||||
|
nymea.Param(robotThingSerialParamTypeId, robot.serial),
|
||||||
|
nymea.Param(robotThingSecretParamTypeId, robot.secret),
|
||||||
|
nymea.Param(robotThingMapIdParamTypeId, mapIDshort)
|
||||||
|
]
|
||||||
|
thingDescriptors.append(thingDescriptor)
|
||||||
|
|
||||||
|
# And let nymea know about all the users robots
|
||||||
|
autoThingsAppeared(thingDescriptors)
|
||||||
|
# return
|
||||||
|
|
||||||
|
# If no poll timer is set up yet, start it now
|
||||||
|
logger.log("Creating polltimer")
|
||||||
|
global pollTimer
|
||||||
|
pollTimer = threading.Timer(5, pollService)
|
||||||
|
pollTimer.start()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# Setup for the robots
|
||||||
|
if info.thing.thingClassId == robotThingClassId:
|
||||||
|
|
||||||
|
serial = info.thing.paramValue(robotThingSerialParamTypeId)
|
||||||
|
secret = info.thing.paramValue(robotThingSecretParamTypeId)
|
||||||
|
robot = Robot(serial, secret, info.thing.name)
|
||||||
|
thingsAndRobots[info.thing] = robot;
|
||||||
|
logger.log(robot.get_robot_state())
|
||||||
|
# set up polling for robot status
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def pollService():
|
||||||
|
logger.log("pollService!!!")
|
||||||
|
|
||||||
|
# Poll all robots we know
|
||||||
|
for thing in myThings():
|
||||||
|
if thing.thingClassId == robotThingClassId:
|
||||||
|
robot = thingsAndRobots[thing]
|
||||||
|
logger.log("polling robot:", robot)
|
||||||
|
|
||||||
|
# Get robot state
|
||||||
|
rbtState = thingsAndRobots[thing].get_robot_state()
|
||||||
|
rbtStateJson = rbtState.json()
|
||||||
|
|
||||||
|
# Set robot docked/charging state
|
||||||
|
rbtStateDetails = rbtStateJson['details']
|
||||||
|
rbtCharging = rbtStateDetails['isCharging']
|
||||||
|
rbtDocked = rbtStateDetails['isDocked']
|
||||||
|
rbtStateOfCharge = rbtStateDetails['charge']
|
||||||
|
logger.log("Updating thing", thing.name, "Charging", rbtCharging)
|
||||||
|
thing.setStateValue(robotChargingStateTypeId, rbtCharging)
|
||||||
|
logger.log("Updating thing", thing.name, "Docked", rbtDocked)
|
||||||
|
thing.setStateValue(robotDockedStateTypeId, rbtDocked)
|
||||||
|
logger.log("Updating thing", thing.name, "Battery Charge Level", rbtStateOfCharge)
|
||||||
|
thing.setStateValue(robotBatteryLevelStateTypeId, rbtStateOfCharge)
|
||||||
|
|
||||||
|
# Set robot cleaning/paused state
|
||||||
|
rbtStateCommands = rbtStateJson['availableCommands']
|
||||||
|
rbtStartAv = rbtStateCommands['start']
|
||||||
|
rbtPauseAv = rbtStateCommands['pause']
|
||||||
|
rbtResumeAv = rbtStateCommands['resume']
|
||||||
|
if rbtStartAv == True:
|
||||||
|
logger.log("Updating thing", thing.name, "Cleaning: False")
|
||||||
|
thing.setStateValue(robotCleaningStateTypeId, False)
|
||||||
|
thing.setStateValue(robotPausedStateTypeId, False)
|
||||||
|
elif rbtPauseAv == True:
|
||||||
|
logger.log("Updating thing", thing.name, "Cleaning: True")
|
||||||
|
thing.setStateValue(robotCleaningStateTypeId, True)
|
||||||
|
thing.setStateValue(robotPausedStateTypeId, False)
|
||||||
|
elif rbtResumeAv == True:
|
||||||
|
logger.log("Updating thing", thing.name, "Paused: True")
|
||||||
|
thing.setStateValue(robotCleaningStateTypeId, True)
|
||||||
|
thing.setStateValue(robotPausedStateTypeId, True)
|
||||||
|
|
||||||
|
# restart the timer for next poll
|
||||||
|
global pollTimer
|
||||||
|
pollTimer = threading.Timer(60, pollService)
|
||||||
|
pollTimer.start()
|
||||||
|
|
||||||
|
|
||||||
|
def executeAction(info):
|
||||||
|
if info.actionTypeId == robotStartCleaningActionTypeId:
|
||||||
|
rbtState = thingsAndRobots[info.thing].get_robot_state()
|
||||||
|
rbtStateJson = rbtState.json()
|
||||||
|
rbtStateCommands = rbtStateJson['availableCommands']
|
||||||
|
rbtStartAv = rbtStateCommands['start']
|
||||||
|
rbtPauseAv = rbtStateCommands['pause']
|
||||||
|
rbtResumeAv = rbtStateCommands['resume']
|
||||||
|
if rbtStartAv == True:
|
||||||
|
logger.log("Start cleaning")
|
||||||
|
thingsAndRobots[info.thing].start_cleaning()
|
||||||
|
elif rbtPauseAv == True:
|
||||||
|
logger.log("Pause cleaning")
|
||||||
|
thingsAndRobots[info.thing].pause_cleaning()
|
||||||
|
elif rbtResumeAv == True:
|
||||||
|
thingsAndRobots[info.thing].resume_cleaning()
|
||||||
|
threading.Timer(5, pollService).start()
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
return
|
||||||
|
|
||||||
|
if info.actionTypeId == robotGoToBaseActionTypeId:
|
||||||
|
thingsAndRobots[info.thing].send_to_base()
|
||||||
|
threading.Timer(5, pollService).start()
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
if info.actionTypeId == robotStopCleaningActionTypeId:
|
||||||
|
thingsAndRobots[info.thing].stop_cleaning()
|
||||||
|
threading.Timer(5, pollService).start()
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
# To do: get available boundaries to use with start_cleaning action
|
||||||
|
if info.actionTypeId == robotGetBoundariesActionTypeId:
|
||||||
|
# rbtMapBound = thingsAndRobots[info.thing].get_map_boundaries()
|
||||||
|
# rbtMapBoundJson = rbtMapBound.json()
|
||||||
|
# logger.log("Robot Map Boundaries", rbtMapBoundJson)
|
||||||
|
threading.Timer(5, pollService).start()
|
||||||
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
|
||||||
|
def deinit():
|
||||||
|
global pollTimer
|
||||||
|
# If we started a poll timer, cancel it on shutdown.
|
||||||
|
if pollTimer is not None:
|
||||||
|
pollTimer.cancel()
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
pybotvac==0.0.20 \
|
||||||
|
--hash=sha256:d4d9d348ee2f3b7472b78bd80f9653406af6e351aa0362ac191055a304930b33 \
|
||||||
|
--hash=sha256:e49a3258f251da0d56764797efb01ba730537b6344ff03b57542142f0640b273
|
||||||
|
setuptools==54.1.1 \
|
||||||
|
--hash=sha256:1ce82798848a978696465866bb3aaab356003c42d6143e1111fcf069ac838274 \
|
||||||
|
--hash=sha256:75c5c4479f4961f1ffdb597c98aa4e4077e6813685025e8bdebf7598aa84e859
|
||||||
|
requests-oauthlib==1.3.0 \
|
||||||
|
--hash=sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d \
|
||||||
|
--hash=sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a \
|
||||||
|
--hash=sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc
|
||||||
|
oauthlib==3.1.0 \
|
||||||
|
--hash=sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889 \
|
||||||
|
--hash=sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea
|
||||||
|
requests==2.25.1 \
|
||||||
|
--hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
|
||||||
|
--hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
|
||||||
|
voluptuous==0.12.1 \
|
||||||
|
--hash=sha256:663572419281ddfaf4b4197fd4942d181630120fb39b333e3adad70aeb56444b \
|
||||||
|
--hash=sha256:8ace33fcf9e6b1f59406bfaf6b8ec7bcc44266a9f29080b4deb4fe6ff2492386
|
||||||
|
urllib3==1.26.3 \
|
||||||
|
--hash=sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80 \
|
||||||
|
--hash=sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73
|
||||||
|
chardet==4.0.0 \
|
||||||
|
--hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
|
||||||
|
--hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
|
||||||
|
certifi==2020.12.5 \
|
||||||
|
--hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c \
|
||||||
|
--hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830
|
||||||
|
launchpadlib==1.10.13 \
|
||||||
|
--hash=sha256:5804d68ec93247194449d17d187e949086da0a4d044f12155fad269ef8515435 \
|
||||||
|
--hash=sha256:f61a591aff60a9315da09131eeb000bfb8287b788d1899a161e727ca22b1989f
|
||||||
|
httplib2==0.19.0 \
|
||||||
|
--hash=sha256:749c32603f9bf16c1277f59531d502e8f1c2ca19901ae653b49c4ed698f0820e \
|
||||||
|
--hash=sha256:e0d428dad43c72dbce7d163b7753ffc7a39c097e6788ef10f4198db69b92f08e
|
||||||
|
keyring==23.0.0 \
|
||||||
|
--hash=sha256:237ff44888ba9b3918a7dcb55c8f1db909c95b6f071bfb46c6918f33f453a68a \
|
||||||
|
--hash=sha256:29f407fd5509c014a6086f17338c70215c8d1ab42d5d49e0254273bc0a64bbfc
|
||||||
|
lazr.uri==1.0.5 \
|
||||||
|
--hash=sha256:f36e7e40d5f8f2cf20ff2c81784a14a546e6c19c216d40a6617ebe0c96c92c49 \
|
||||||
|
--hash=sha256:71f2faf04b148cf63d78da08ee5d8d6a7a7dbda8c9016b389a16f790d080c06f
|
||||||
|
six==1.15.0 \
|
||||||
|
--hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \
|
||||||
|
--hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced
|
||||||
|
testresources==2.0.1 \
|
||||||
|
--hash=sha256:67a361c3a2412231963b91ab04192209aa91a1aa052f0ab87245dbea889d1282 \
|
||||||
|
--hash=sha256:ee9d1982154a1e212d4e4bac6b610800bfb558e4fb853572a827bc14a96e4417
|
||||||
|
wadllib==1.3.5 \
|
||||||
|
--hash=sha256:84fecbaec2fef5ae2d7717a8115d271f18c6b5441eac861c58be8ca57f63c1d3 \
|
||||||
|
--hash=sha256:67d3102b40eefdd6c3007cfbcc4c07f6948fec0666ba5c17d703eab21f054692
|
||||||
|
lazr.restfulclient==0.14.3 \
|
||||||
|
--hash=sha256:9f28bbb7c00374159376bd4ce36b4dacde7c6b86a0af625aa5e3ae214651a690 \
|
||||||
|
--hash=sha256:2320e6d132c9a5148895e85be03274bc9305e4605439b03541ee3a618e00fb94
|
||||||
|
pyparsing==2.4.7 \
|
||||||
|
--hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \
|
||||||
|
--hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b
|
||||||
|
jeepney==0.6.0 \
|
||||||
|
--hash=sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657 \
|
||||||
|
--hash=sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae
|
||||||
|
importlib-metadata==3.7.0 \
|
||||||
|
--hash=sha256:24499ffde1b80be08284100393955842be4a59c7c16bbf2738aad0e464a8e0aa \
|
||||||
|
--hash=sha256:c6af5dbf1126cd959c4a8d8efd61d4d3c83bddb0459a17e554284a077574b614
|
||||||
|
pbr==5.5.1 \
|
||||||
|
--hash=sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9 \
|
||||||
|
--hash=sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00
|
||||||
|
SecretStorage==3.3.1 \
|
||||||
|
--hash=sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f \
|
||||||
|
--hash=sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195
|
||||||
|
distro==1.5.0 \
|
||||||
|
--hash=sha256:0e58756ae38fbd8fc3020d54badb8eae17c5b9dcbed388b17bb55b8a5928df92 \
|
||||||
|
--hash=sha256:df74eed763e18d10d0da624258524ae80486432cd17392d9c3d96f5e83cd2799
|
||||||
|
zipp==3.4.1 \
|
||||||
|
--hash=sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76 \
|
||||||
|
--hash=sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098
|
||||||
|
cryptography==3.4.6 \
|
||||||
|
--hash=sha256:066bc53f052dfeda2f2d7c195cf16fb3e5ff13e1b6b7415b468514b40b381a5b \
|
||||||
|
--hash=sha256:0923ba600d00718d63a3976f23cab19aef10c1765038945628cd9be047ad0336 \
|
||||||
|
--hash=sha256:2d32223e5b0ee02943f32b19245b61a62db83a882f0e76cc564e1cec60d48f87 \
|
||||||
|
--hash=sha256:4169a27b818de4a1860720108b55a2801f32b6ae79e7f99c00d79f2a2822eeb7 \
|
||||||
|
--hash=sha256:57ad77d32917bc55299b16d3b996ffa42a1c73c6cfa829b14043c561288d2799 \
|
||||||
|
--hash=sha256:5ecf2bcb34d17415e89b546dbb44e73080f747e504273e4d4987630493cded1b \
|
||||||
|
--hash=sha256:600cf9bfe75e96d965509a4c0b2b183f74a4fa6f5331dcb40fb7b77b7c2484df \
|
||||||
|
--hash=sha256:66b57a9ca4b3221d51b237094b0303843b914b7d5afd4349970bb26518e350b0 \
|
||||||
|
--hash=sha256:93cfe5b7ff006de13e1e89830810ecbd014791b042cbe5eec253be11ac2b28f3 \
|
||||||
|
--hash=sha256:9e98b452132963678e3ac6c73f7010fe53adf72209a32854d55690acac3f6724 \
|
||||||
|
--hash=sha256:df186fcbf86dc1ce56305becb8434e4b6b7504bc724b71ad7a3239e0c9d14ef2 \
|
||||||
|
--hash=sha256:fec7fb46b10da10d9e1d078d1ff8ed9e05ae14f431fdbd11145edd0550b9a964 \
|
||||||
|
--hash=sha256:926ae3dfd160050158b9ca25d419fb7ee658974564b01aa10c059a75dffab7e8
|
||||||
|
cffi==1.14.5 \
|
||||||
|
--hash=sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813 \
|
||||||
|
--hash=sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06 \
|
||||||
|
--hash=sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea \
|
||||||
|
--hash=sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee \
|
||||||
|
--hash=sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396 \
|
||||||
|
--hash=sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73 \
|
||||||
|
--hash=sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315 \
|
||||||
|
--hash=sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1 \
|
||||||
|
--hash=sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49 \
|
||||||
|
--hash=sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892 \
|
||||||
|
--hash=sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482 \
|
||||||
|
--hash=sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058 \
|
||||||
|
--hash=sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5 \
|
||||||
|
--hash=sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53 \
|
||||||
|
--hash=sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045 \
|
||||||
|
--hash=sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3 \
|
||||||
|
--hash=sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5 \
|
||||||
|
--hash=sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e \
|
||||||
|
--hash=sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c \
|
||||||
|
--hash=sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369 \
|
||||||
|
--hash=sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827 \
|
||||||
|
--hash=sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053 \
|
||||||
|
--hash=sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa \
|
||||||
|
--hash=sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4 \
|
||||||
|
--hash=sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322 \
|
||||||
|
--hash=sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132 \
|
||||||
|
--hash=sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62 \
|
||||||
|
--hash=sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa \
|
||||||
|
--hash=sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0 \
|
||||||
|
--hash=sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396 \
|
||||||
|
--hash=sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e \
|
||||||
|
--hash=sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991 \
|
||||||
|
--hash=sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6 \
|
||||||
|
--hash=sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1 \
|
||||||
|
--hash=sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406 \
|
||||||
|
--hash=sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d \
|
||||||
|
--hash=sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c \
|
||||||
|
--hash=sha256:9338beed13d880320450d95c9e07ccf839faa3ea7b75d788f4ed46d845044a71
|
||||||
|
pycparser==2.20 \
|
||||||
|
--hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \
|
||||||
|
--hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705
|
||||||
|
idna==2.5 \
|
||||||
|
--hash=sha256:3cb5ce08046c4e3a560fc02f138d0ac63e00f8ce5901a56b32ec8b7994082aab \
|
||||||
|
--hash=sha256:cc19709fd6d0cbfed39ea875d29ba6d4e22c0cebc510a76d6302a28385e8bb70
|
||||||
|
typing-extensions==3.7.4.3 \
|
||||||
|
--hash=sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918 \
|
||||||
|
--hash=sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c \
|
||||||
|
--hash=sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f
|
||||||
Loading…
Reference in New Issue