Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 49 additions & 40 deletions homeassistant/components/citybikes/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
CITYBIKES_ATTRIBUTION = "Information provided by the CityBikes Project "\
"(https://citybik.es/#about)"

CITYBIKES_NETWORKS = 'citybikes_networks'

PLATFORM_SCHEMA = vol.All(
cv.has_at_least_one_key(CONF_RADIUS, CONF_STATIONS_LIST),
PLATFORM_SCHEMA.extend({
Expand All @@ -72,12 +74,12 @@
vol.Required(ATTR_LOCATION): vol.Schema({
vol.Required(ATTR_LATITUDE): cv.latitude,
vol.Required(ATTR_LONGITUDE): cv.longitude,
}, extra=vol.REMOVE_EXTRA),
}, extra=vol.REMOVE_EXTRA)
}, extra=vol.REMOVE_EXTRA),
}, extra=vol.REMOVE_EXTRA)

NETWORKS_RESPONSE_SCHEMA = vol.Schema({
vol.Required(ATTR_NETWORKS_LIST): [NETWORK_SCHEMA],
})
})

STATION_SCHEMA = vol.Schema({
vol.Required(ATTR_FREE_BIKES): cv.positive_int,
Expand All @@ -89,13 +91,13 @@
vol.Required(ATTR_TIMESTAMP): cv.string,
vol.Optional(ATTR_EXTRA):
vol.Schema({vol.Optional(ATTR_UID): cv.string}, extra=vol.REMOVE_EXTRA)
}, extra=vol.REMOVE_EXTRA)
}, extra=vol.REMOVE_EXTRA)

STATIONS_RESPONSE_SCHEMA = vol.Schema({
vol.Required(ATTR_NETWORK): vol.Schema({
vol.Required(ATTR_STATIONS_LIST): [STATION_SCHEMA]
}, extra=vol.REMOVE_EXTRA)
})
}, extra=vol.REMOVE_EXTRA)
})


class CityBikesRequestError(Exception):
Expand Down Expand Up @@ -135,18 +137,21 @@ async def async_setup_platform(hass, config, async_add_entities,
network_id = config.get(CONF_NETWORK)
stations_list = set(config.get(CONF_STATIONS_LIST, []))
radius = config.get(CONF_RADIUS, 0)
name = config.get(CONF_NAME)
name = config[CONF_NAME]
if not hass.config.units.is_metric:
radius = distance.convert(radius, LENGTH_FEET, LENGTH_METERS)

# Create a single instance of CityBikesNetworks.
networks = hass.data.setdefault(
CITYBIKES_NETWORKS, CityBikesNetworks(hass))

if not network_id:
network_id = await CityBikesNetwork.get_closest_network_id(
hass, latitude, longitude)
network_id = await networks.get_closest_network_id(latitude, longitude)

if network_id not in hass.data[PLATFORM][MONITORED_NETWORKS]:
network = CityBikesNetwork(hass, network_id)
hass.data[PLATFORM][MONITORED_NETWORKS][network_id] = network
hass.async_add_job(network.async_refresh)
hass.async_create_task(network.async_refresh())
async_track_time_interval(hass, network.async_refresh, SCAN_INTERVAL)
else:
network = hass.data[PLATFORM][MONITORED_NETWORKS][network_id]
Expand All @@ -163,29 +168,37 @@ async def async_setup_platform(hass, config, async_add_entities,

if radius > dist or stations_list.intersection(
(station_id, station_uid)):
devices.append(CityBikesStation(hass, network, station_id, name))
if name:
uid = "_".join([network.network_id, name, station_id])
else:
uid = "_".join([network.network_id, station_id])
entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, uid, hass=hass)
devices.append(CityBikesStation(network, station_id, entity_id))

async_add_entities(devices, True)


class CityBikesNetwork:
"""Thin wrapper around a CityBikes network object."""
class CityBikesNetworks:
"""Represent all CityBikes networks."""

NETWORKS_LIST = None
NETWORKS_LIST_LOADING = asyncio.Condition()
def __init__(self, hass):
"""Initialize the networks instance."""
self.hass = hass
self.networks = None
self.networks_loading = asyncio.Condition(loop=hass.loop)

@classmethod
async def get_closest_network_id(cls, hass, latitude, longitude):
async def get_closest_network_id(self, latitude, longitude):
"""Return the id of the network closest to provided location."""
try:
await cls.NETWORKS_LIST_LOADING.acquire()
if cls.NETWORKS_LIST is None:
await self.networks_loading.acquire()
if self.networks is None:
networks = await async_citybikes_request(
hass, NETWORKS_URI, NETWORKS_RESPONSE_SCHEMA)
cls.NETWORKS_LIST = networks[ATTR_NETWORKS_LIST]
self.hass, NETWORKS_URI, NETWORKS_RESPONSE_SCHEMA)
self.networks = networks[ATTR_NETWORKS_LIST]
result = None
minimum_dist = None
for network in cls.NETWORKS_LIST:
for network in self.networks:
network_latitude = network[ATTR_LOCATION][ATTR_LATITUDE]
network_longitude = network[ATTR_LOCATION][ATTR_LONGITUDE]
dist = location.distance(
Expand All @@ -198,14 +211,18 @@ async def get_closest_network_id(cls, hass, latitude, longitude):
except CityBikesRequestError:
raise PlatformNotReady
finally:
cls.NETWORKS_LIST_LOADING.release()
self.networks_loading.release()


class CityBikesNetwork:
"""Thin wrapper around a CityBikes network object."""

def __init__(self, hass, network_id):
"""Initialize the network object."""
self.hass = hass
self.network_id = network_id
self.stations = []
self.ready = asyncio.Event()
self.ready = asyncio.Event(loop=hass.loop)

async def async_refresh(self, now=None):
"""Refresh the state of the network."""
Expand All @@ -225,37 +242,29 @@ async def async_refresh(self, now=None):
class CityBikesStation(Entity):
"""CityBikes API Sensor."""

def __init__(self, hass, network, station_id, base_name=''):
def __init__(self, network, station_id, entity_id):
"""Initialize the sensor."""
self._network = network
self._station_id = station_id
self._station_data = {}
if base_name:
uid = "_".join([network.network_id, base_name, station_id])
else:
uid = "_".join([network.network_id, station_id])
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, uid, hass=hass)
self.entity_id = entity_id

@property
def state(self):
"""Return the state of the sensor."""
return self._station_data.get(ATTR_FREE_BIKES, None)
return self._station_data.get(ATTR_FREE_BIKES)

@property
def name(self):
"""Return the name of the sensor."""
if ATTR_NAME in self._station_data:
return self._station_data[ATTR_NAME]
return None
return self._station_data.get(ATTR_NAME)

async def async_update(self):
"""Update station state."""
if self._network.ready.is_set():
for station in self._network.stations:
if station[ATTR_ID] == self._station_id:
self._station_data = station
break
for station in self._network.stations:
if station[ATTR_ID] == self._station_id:
self._station_data = station
break

@property
def device_state_attributes(self):
Expand Down